summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp7
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java19
-rw-r--r--apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java14
-rw-r--r--apct-tests/perftests/surfaceflinger/AndroidManifest.xml4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java161
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java36
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java38
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java44
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Scribe.java24
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp6
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl1
-rw-r--r--cmds/idmap2/include/idmap2/FabricatedOverlay.h6
-rw-r--r--cmds/idmap2/include/idmap2/ResourceContainer.h2
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h3
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h5
-rw-r--r--cmds/idmap2/libidmap2/FabricatedOverlay.cpp50
-rw-r--r--cmds/idmap2/libidmap2/ResourceContainer.cpp6
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp6
-rw-r--r--cmds/idmap2/libidmap2/proto/fabricated_v1.proto1
-rw-r--r--cmds/idmap2/tests/FabricatedOverlayTests.cpp64
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp6
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp6
-rw-r--r--core/api/current.txt9
-rw-r--r--core/api/system-current.txt3
-rw-r--r--core/api/test-current.txt7
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java21
-rw-r--r--core/java/android/app/AppOpsManager.java1
-rw-r--r--core/java/android/app/NotificationChannelGroup.java21
-rw-r--r--core/java/android/app/UiAutomationConnection.java1
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java16
-rw-r--r--core/java/android/companion/ICompanionDeviceManager.aidl2
-rw-r--r--core/java/android/content/om/FabricatedOverlay.java44
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java11
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java54
-rw-r--r--core/java/android/os/BatteryStats.java20
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/PowerManager.java38
-rw-r--r--core/java/android/os/UserManager.java15
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/util/FeatureFlagUtils.java9
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java77
-rw-r--r--core/java/android/view/IWindowManager.aidl5
-rw-r--r--core/java/android/view/InsetsState.java14
-rw-r--r--core/java/android/view/View.java138
-rw-r--r--core/java/android/view/ViewGroup.java8
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/android/view/WindowLayout.java18
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java57
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java35
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java25
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java24
-rw-r--r--core/java/android/view/accessibility/DirectAccessibilityConnection.java4
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java7
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java4
-rw-r--r--core/java/android/widget/LinearLayout.java64
-rw-r--r--core/java/android/widget/RatingBar.java29
-rw-r--r--core/java/android/widget/TextView.java31
-rw-r--r--core/java/android/window/TransitionInfo.java11
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java15
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java9
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java1316
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java8
-rw-r--r--core/jni/Android.bp5
-rw-r--r--core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp46
-rw-r--r--core/jni/com_android_internal_os_LongMultiStateCounter.cpp38
-rw-r--r--core/proto/android/os/tombstone.proto30
-rw-r--r--core/res/Android.bp60
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java12
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java142
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java44
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java255
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt143
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt19
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt177
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt4
-rw-r--r--media/Android.bp73
-rw-r--r--media/aidl/android/media/audio/common/AudioAttributes.aidl59
-rw-r--r--media/aidl/android/media/audio/common/AudioFlag.aidl116
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioAttributes.aidl42
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFlag.aidl54
-rw-r--r--media/java/android/media/AudioManager.java4
-rw-r--r--media/java/android/media/SoundPool.java4
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java13
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_tv_Tuner.cpp64
-rw-r--r--media/jni/android_media_tv_Tuner.h3
-rw-r--r--media/jni/soundpool/SoundPool.cpp5
-rw-r--r--media/jni/soundpool/SoundPool.h2
-rw-r--r--media/jni/soundpool/Stream.cpp15
-rw-r--r--media/jni/soundpool/Stream.h5
-rw-r--r--media/jni/soundpool/StreamManager.cpp11
-rw-r--r--media/jni/soundpool/StreamManager.h2
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp13
-rw-r--r--media/jni/tuner/TunerClient.cpp7
-rw-r--r--media/jni/tuner/TunerClient.h7
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml6
-rw-r--r--packages/SettingsLib/Spa/gallery/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt32
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt13
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt5
-rw-r--r--packages/SettingsLib/SpaPrivileged/res/values/strings.xml8
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt5
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt20
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt19
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt13
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt86
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt15
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt31
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt106
-rw-r--r--packages/SettingsLib/res/values/strings.xml15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java137
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java47
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java12
-rw-r--r--packages/SystemUI/TEST_MAPPING11
-rw-r--r--packages/SystemUI/res/raw/fingerprint_dialogue_error_to_success_lottie.json1
-rw-r--r--packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_success_lottie.json1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt690
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java307
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt237
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java175
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java1
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java15
-rw-r--r--services/core/java/com/android/server/BootReceiver.java50
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java15
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java77
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java80
-rw-r--r--services/core/java/com/android/server/am/BroadcastDispatcher.java4
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2562
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java1960
-rw-r--r--services/core/java/com/android/server/am/BroadcastSkipPolicy.java715
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java15
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java14
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java26
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java44
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java18
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java42
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java9
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java12
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java13
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java92
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java107
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java55
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java15
-rw-r--r--services/core/java/com/android/server/os/NativeTombstoneManager.java11
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageCacher.java9
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java76
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java25
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java11
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java10
-rw-r--r--services/core/java/com/android/server/power/Notifier.java3
-rw-r--r--services/core/java/com/android/server/power/PowerGroup.java27
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java45
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java2
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java1807
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java16
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java33
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java16
-rw-r--r--services/core/java/com/android/server/textservices/TextServicesManagerService.java52
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java10
-rw-r--r--services/core/java/com/android/server/vibrator/AbstractVibratorStep.java92
-rw-r--r--services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java24
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java18
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java15
-rw-r--r--services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java21
-rw-r--r--services/core/java/com/android/server/vibrator/RampOffVibratorStep.java12
-rw-r--r--services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java68
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java13
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java19
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java30
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java32
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java154
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java260
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java11
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java11
-rw-r--r--services/core/java/com/android/server/wm/Transition.java232
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java60
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java50
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java7
-rw-r--r--services/proguard.flags3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java140
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt6
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java203
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java91
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java7
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java10
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java44
-rw-r--r--tests/testables/tests/src/android/testing/TestableLooperTest.java65
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp16
-rw-r--r--tools/aapt2/integration-tests/DumpTest/components.apkbin84534 -> 92659 bytes
-rw-r--r--tools/aapt2/integration-tests/DumpTest/components_full_proto.txt4
-rw-r--r--tools/lint/OWNERS1
284 files changed, 10686 insertions, 6497 deletions
diff --git a/Android.bp b/Android.bp
index 30b38d302a2d..c0a70b99827e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -255,7 +255,10 @@ filegroup {
java_defaults {
name: "framework-minus-apex-defaults",
- defaults: ["framework-aidl-export-defaults"],
+ defaults: [
+ "framework-aidl-export-defaults",
+ "latest_android_hardware_soundtrigger3_java_static",
+ ],
srcs: [
":framework-non-updatable-sources",
"core/java/**/*.logtags",
@@ -365,8 +368,6 @@ java_defaults {
sdk_version: "core_platform",
static_libs: [
"android.hardware.common.fmq-V1-java",
- // TODO(b/184162091)
- "android.hardware.soundtrigger3-V1-java",
"bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index d5c386fc5368..a65205500651 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -424,7 +424,7 @@ public class UserLifecycleTests {
final int userId = createManagedProfile();
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
- stopUser(userId, true);
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -479,7 +479,7 @@ public class UserLifecycleTests {
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
- stopUser(userId, true);
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -676,6 +676,19 @@ public class UserLifecycleTests {
return success[0];
}
+ /**
+ * Waits for broadcast idle before stopping a user, to prevent timeouts on stop user.
+ * Stopping a user heavily depends on broadcast queue, and that gets crowded after user creation
+ * or user switches, which leads to a timeout on stopping user and cause the tests to be flaky.
+ * Do not call this method while timing is on. i.e. between mRunner.resumeTiming() and
+ * mRunner.pauseTiming(). Otherwise it would cause the test results to be spiky.
+ */
+ private void stopUserAfterWaitingForBroadcastIdle(int userId, boolean force)
+ throws RemoteException {
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle");
+ stopUser(userId, force);
+ }
+
private void stopUser(int userId, boolean force) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@@ -711,7 +724,7 @@ public class UserLifecycleTests {
attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser());
if (stopNewUser) {
- stopUser(testUser, true);
+ stopUserAfterWaitingForBroadcastIdle(testUser, true);
attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser));
}
diff --git a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
index 81685cbbd594..33b2bea1da66 100644
--- a/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
+++ b/apct-tests/perftests/rubidium/src/android/rubidium/js/JSScriptEnginePerfTests.java
@@ -37,6 +37,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.adservices.service.js.IsolateSettings;
import com.android.adservices.service.js.JSScriptArgument;
import com.android.adservices.service.js.JSScriptArrayArgument;
import com.android.adservices.service.js.JSScriptEngine;
@@ -314,7 +315,11 @@ public class JSScriptEnginePerfTests {
@NonNull CountDownLatch resultLatch) {
Objects.requireNonNull(engine);
Objects.requireNonNull(resultLatch);
- ListenableFuture<String> result = engine.evaluate(jsScript, args, functionName);
+ ListenableFuture<String> result = engine.evaluate(
+ jsScript,
+ args,
+ functionName,
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
@@ -328,7 +333,12 @@ public class JSScriptEnginePerfTests {
@NonNull CountDownLatch resultLatch) {
Objects.requireNonNull(engine);
Objects.requireNonNull(resultLatch);
- ListenableFuture<String> result = engine.evaluate(jsScript, wasmScript, args, functionName);
+ ListenableFuture<String> result = engine.evaluate(
+ jsScript,
+ wasmScript,
+ args,
+ functionName,
+ IsolateSettings.forMaxHeapSizeEnforcementDisabled());
result.addListener(resultLatch::countDown, sExecutorService);
return result;
}
diff --git a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
index c908d6ae4cff..26f25863f055 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
@@ -16,6 +16,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.perftests.surfaceflinger">
+ <!-- permission needed to write perfetto trace and read/write simpleperf report -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
<application android:label="SurfaceFlingerPerfTests">
<uses-library android:name="android.test.runner" />
<activity android:name="android.surfaceflinger.SurfaceFlingerTestActivity"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 3ca1ad58955d..9d364782a57f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -16,6 +16,7 @@
package com.android.server.job.controllers;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -31,6 +32,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -80,31 +82,26 @@ public final class FlexibilityController extends StateController {
private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
/**
- * Keeps track of what flexible constraints are satisfied at the moment.
- * Is updated by the other controllers.
- */
- @VisibleForTesting
- @GuardedBy("mLock")
- int mSatisfiedFlexibleConstraints;
-
- /** Hard cutoff to remove flexible constraints. */
- private long mDeadlineProximityLimitMs =
- FcConfig.DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
-
- /**
* The default deadline that all flexible constraints should be dropped by if a job lacks
* a deadline.
*/
private long mFallbackFlexibilityDeadlineMs =
FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
- @GuardedBy("mLock")
+ private long mRescheduledJobDeadline = FcConfig.DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
+ private long mMaxRescheduledDeadline = FcConfig.DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
+
@VisibleForTesting
+ @GuardedBy("mLock")
boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
private long mMinTimeBetweenFlexibilityAlarmsMs =
FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+ /** Hard cutoff to remove flexible constraints. */
+ private long mDeadlineProximityLimitMs =
+ FcConfig.DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
+
/**
* The percent of a job's lifecycle to drop number of required constraints.
* mPercentToDropConstraints[i] denotes that at x% of a Jobs lifecycle,
@@ -113,6 +110,17 @@ public final class FlexibilityController extends StateController {
private int[] mPercentToDropConstraints;
@VisibleForTesting
+ boolean mDeviceSupportsFlexConstraints;
+
+ /**
+ * Keeps track of what flexible constraints are satisfied at the moment.
+ * Is updated by the other controllers.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ int mSatisfiedFlexibleConstraints;
+
+ @VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
@VisibleForTesting
@@ -120,7 +128,6 @@ public final class FlexibilityController extends StateController {
final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
@VisibleForTesting
final FcConfig mFcConfig;
-
@VisibleForTesting
final PrefetchController mPrefetchController;
@@ -168,6 +175,9 @@ public final class FlexibilityController extends StateController {
public FlexibilityController(
JobSchedulerService service, PrefetchController prefetchController) {
super(service);
+ mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ mFlexibilityEnabled &= mDeviceSupportsFlexConstraints;
mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
mFcConfig = new FcConfig();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
@@ -187,10 +197,14 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus js, JobStatus lastJob) {
if (js.hasFlexibilityConstraint()) {
- mFlexibilityTracker.add(js);
- js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (!mDeviceSupportsFlexConstraints) {
+ js.setFlexibilityConstraintSatisfied(nowElapsed, true);
+ return;
+ }
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
+ mFlexibilityTracker.add(js);
+ js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
}
@@ -321,6 +335,12 @@ public final class FlexibilityController extends StateController {
// There is no deadline and no estimated launch time.
return NO_LIFECYCLE_END;
}
+ if (js.getNumFailures() > 1) {
+ // Number of failures will not equal one as per restriction in JobStatus constructor.
+ return earliest + Math.min(
+ (long) Math.scalb(mRescheduledJobDeadline, js.getNumFailures() - 2),
+ mMaxRescheduledDeadline);
+ }
return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
? earliest + mFallbackFlexibilityDeadlineMs : js.getLatestRunTimeElapsed();
}
@@ -340,7 +360,6 @@ public final class FlexibilityController extends StateController {
return percentInTime;
}
- /** The elapsed time that marks when the next constraint should be dropped. */
@VisibleForTesting
@ElapsedRealtimeLong
@GuardedBy("mLock")
@@ -351,7 +370,6 @@ public final class FlexibilityController extends StateController {
}
/** The elapsed time that marks when the next constraint should be dropped. */
- @VisibleForTesting
@ElapsedRealtimeLong
@GuardedBy("mLock")
long getNextConstraintDropTimeElapsedLocked(JobStatus js, long earliest, long latest) {
@@ -406,7 +424,7 @@ public final class FlexibilityController extends StateController {
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
synchronized (mLock) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- for (int j = 1; j <= mFlexibilityTracker.size(); j++) {
+ for (int j = 0; j < mFlexibilityTracker.size(); j++) {
final ArraySet<JobStatus> jobs = mFlexibilityTracker
.getJobsByNumRequiredConstraints(j);
for (int i = 0; i < jobs.size(); i++) {
@@ -445,7 +463,7 @@ public final class FlexibilityController extends StateController {
FlexibilityTracker(int numFlexibleConstraints) {
mTrackedJobs = new ArrayList<>();
- for (int i = 0; i < numFlexibleConstraints; i++) {
+ for (int i = 0; i <= numFlexibleConstraints; i++) {
mTrackedJobs.add(new ArraySet<JobStatus>());
}
}
@@ -457,15 +475,15 @@ public final class FlexibilityController extends StateController {
Slog.wtfStack(TAG, "Asked for a larger number of constraints than exists.");
return null;
}
- return mTrackedJobs.get(numRequired - 1);
+ return mTrackedJobs.get(numRequired);
}
/** adds a JobStatus object based on number of required flexible constraints. */
public void add(JobStatus js) {
- if (js.getNumRequiredFlexibleConstraints() <= 0) {
+ if (js.getNumRequiredFlexibleConstraints() < 0) {
return;
}
- mTrackedJobs.get(js.getNumRequiredFlexibleConstraints() - 1).add(js);
+ mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).add(js);
}
/** Removes a JobStatus object. */
@@ -473,7 +491,7 @@ public final class FlexibilityController extends StateController {
if (js.getNumRequiredFlexibleConstraints() == 0) {
return;
}
- mTrackedJobs.get(js.getNumRequiredFlexibleConstraints() - 1).remove(js);
+ mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).remove(js);
}
public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) {
@@ -498,21 +516,15 @@ public final class FlexibilityController extends StateController {
/**
* Adjusts number of required flexible constraints and sorts it into the tracker.
* Returns false if the job status's number of flexible constraints is now 0.
- * Jobs with 0 required flexible constraints are removed from the tracker.
*/
- public boolean adjustJobsRequiredConstraints(JobStatus js, int n, long nowElapsed) {
- if (n == 0) {
- return false;
- }
- remove(js);
- js.adjustNumRequiredFlexibleConstraints(n);
- js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
- if (js.getNumRequiredFlexibleConstraints() <= 0) {
- maybeStopTrackingJobLocked(js, null, false);
- return false;
+ public boolean adjustJobsRequiredConstraints(JobStatus js, int adjustBy, long nowElapsed) {
+ if (adjustBy != 0) {
+ remove(js);
+ js.adjustNumRequiredFlexibleConstraints(adjustBy);
+ js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
+ add(js);
}
- add(js);
- return true;
+ return js.getNumRequiredFlexibleConstraints() > 0;
}
public int size() {
@@ -531,6 +543,8 @@ public final class FlexibilityController extends StateController {
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
+ pw.print(" Num Required Constraints: ");
+ pw.print(js.getNumRequiredFlexibleConstraints());
pw.println();
}
}
@@ -551,20 +565,24 @@ public final class FlexibilityController extends StateController {
}
public void scheduleDropNumConstraintsAlarm(JobStatus js, long nowElapsed) {
- long nextTimeElapsed;
synchronized (mLock) {
final long earliest = getLifeCycleBeginningElapsedLocked(js);
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
- nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ final long nextTimeElapsed =
+ getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+
+ if (latest - nowElapsed < mDeadlineProximityLimitMs) {
+ mFlexibilityTracker.adjustJobsRequiredConstraints(js,
+ -js.getNumRequiredFlexibleConstraints(), nowElapsed);
+ return;
+ }
if (nextTimeElapsed == NO_LIFECYCLE_END) {
// There is no known or estimated next time to drop a constraint.
removeAlarmForKey(js);
return;
}
-
if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) {
- mFlexibilityTracker.adjustJobsRequiredConstraints(
- js, -js.getNumRequiredFlexibleConstraints(), nowElapsed);
+ addAlarm(js, latest - mDeadlineProximityLimitMs);
return;
}
addAlarm(js, nextTimeElapsed);
@@ -579,20 +597,8 @@ public final class FlexibilityController extends StateController {
for (int i = 0; i < expired.size(); i++) {
JobStatus js = expired.valueAt(i);
boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
-
- final long earliest = getLifeCycleBeginningElapsedLocked(js);
- final long latest = getLifeCycleEndElapsedLocked(js, earliest);
-
- if (latest - nowElapsed < mDeadlineProximityLimitMs) {
- mFlexibilityTracker.adjustJobsRequiredConstraints(js,
- -js.getNumRequiredFlexibleConstraints(), nowElapsed);
- } else {
- long nextTimeElapsed =
- getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)
- && nextTimeElapsed != NO_LIFECYCLE_END) {
- mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
- }
+ if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) {
+ scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
changedJobs.add(js);
@@ -619,6 +625,10 @@ public final class FlexibilityController extends StateController {
FC_CONFIG_PREFIX + "min_alarm_time_flexibility_ms";
static final String KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
FC_CONFIG_PREFIX + "percents_to_drop_num_flexible_constraints";
+ static final String KEY_MAX_RESCHEDULED_DEADLINE_MS =
+ FC_CONFIG_PREFIX + "max_rescheduled_deadline_ms";
+ static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
+ FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
@VisibleForTesting
@@ -628,6 +638,8 @@ public final class FlexibilityController extends StateController {
private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS;
@VisibleForTesting
final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
+ private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS;
+ private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS;
/**
* If false the controller will not track new jobs
@@ -643,13 +655,18 @@ public final class FlexibilityController extends StateController {
/** The percentages of a jobs' lifecycle to drop the number of required constraints. */
public int[] PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ /** Initial fallback flexible deadline for rescheduled jobs. */
+ public long RESCHEDULED_JOB_DEADLINE_MS = DEFAULT_RESCHEDULED_JOB_DEADLINE_MS;
+ /** The max deadline for rescheduled jobs. */
+ public long MAX_RESCHEDULED_DEADLINE_MS = DEFAULT_MAX_RESCHEDULED_DEADLINE_MS;
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
case KEY_FLEXIBILITY_ENABLED:
- FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED);
+ FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED)
+ && mDeviceSupportsFlexConstraints;
if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) {
mFlexibilityEnabled = FLEXIBILITY_ENABLED;
mShouldReevaluateConstraints = true;
@@ -662,6 +679,22 @@ public final class FlexibilityController extends StateController {
}
}
break;
+ case KEY_RESCHEDULED_JOB_DEADLINE_MS:
+ RESCHEDULED_JOB_DEADLINE_MS =
+ properties.getLong(key, DEFAULT_RESCHEDULED_JOB_DEADLINE_MS);
+ if (mRescheduledJobDeadline != RESCHEDULED_JOB_DEADLINE_MS) {
+ mRescheduledJobDeadline = RESCHEDULED_JOB_DEADLINE_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_RESCHEDULED_DEADLINE_MS:
+ MAX_RESCHEDULED_DEADLINE_MS =
+ properties.getLong(key, DEFAULT_MAX_RESCHEDULED_DEADLINE_MS);
+ if (mMaxRescheduledDeadline != MAX_RESCHEDULED_DEADLINE_MS) {
+ mMaxRescheduledDeadline = MAX_RESCHEDULED_DEADLINE_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_DEADLINE_PROXIMITY_LIMIT:
DEADLINE_PROXIMITY_LIMIT_MS =
properties.getLong(key, DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS);
@@ -733,6 +766,14 @@ public final class FlexibilityController extends StateController {
pw.increaseIndent();
pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println();
+ pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println();
+ pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println();
+ pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS,
+ MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS).println();
+ pw.print(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS,
+ PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS).println();
+ pw.print(KEY_RESCHEDULED_JOB_DEADLINE_MS, RESCHEDULED_JOB_DEADLINE_MS).println();
+ pw.print(KEY_MAX_RESCHEDULED_DEADLINE_MS, MAX_RESCHEDULED_DEADLINE_MS).println();
pw.decreaseIndent();
}
@@ -748,9 +789,15 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
pw.println("# Constraints Satisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints));
+ pw.print("Satisfied Flexible Constraints: ");
+ JobStatus.dumpConstraints(pw, mSatisfiedFlexibleConstraints);
+ pw.println();
pw.println();
mFlexibilityTracker.dump(pw, predicate);
+ pw.println();
+ mFlexibilityAlarmQueue.dump(pw);
+ pw.println();
mFcConfig.dump(pw);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 4ce6b32166c1..4320db09aef5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -572,8 +572,11 @@ public final class JobStatus {
(latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
>= MIN_WINDOW_FOR_FLEXIBILITY_MS;
+ // The first time a job is rescheduled it will not be subject to flexible constraints.
+ // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
if (!isRequestedExpeditedJob()
&& satisfiesMinWindowException
+ && numFailures != 1
&& lacksSomeFlexibleConstraints) {
mNumRequiredFlexibleConstraints =
NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
@@ -1928,35 +1931,38 @@ public final class JobStatus {
proto.end(token);
}
- void dumpConstraints(PrintWriter pw, int constraints) {
- if ((constraints&CONSTRAINT_CHARGING) != 0) {
+ static void dumpConstraints(PrintWriter pw, int constraints) {
+ if ((constraints & CONSTRAINT_CHARGING) != 0) {
pw.print(" CHARGING");
}
- if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) {
+ if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
pw.print(" BATTERY_NOT_LOW");
}
- if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) {
+ if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
pw.print(" STORAGE_NOT_LOW");
}
- if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
+ if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
pw.print(" TIMING_DELAY");
}
- if ((constraints&CONSTRAINT_DEADLINE) != 0) {
+ if ((constraints & CONSTRAINT_DEADLINE) != 0) {
pw.print(" DEADLINE");
}
- if ((constraints&CONSTRAINT_IDLE) != 0) {
+ if ((constraints & CONSTRAINT_IDLE) != 0) {
pw.print(" IDLE");
}
- if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
+ if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
pw.print(" CONNECTIVITY");
}
- if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
+ if ((constraints & CONSTRAINT_FLEXIBLE) != 0) {
+ pw.print(" FLEXIBILITY");
+ }
+ if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
pw.print(" CONTENT_TRIGGER");
}
- if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
+ if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
pw.print(" DEVICE_NOT_DOZING");
}
- if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
pw.print(" BACKGROUND_NOT_RESTRICTED");
}
if ((constraints & CONSTRAINT_PREFETCH) != 0) {
@@ -2242,6 +2248,14 @@ public final class JobStatus {
((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH)
& ~satisfiedConstraints));
pw.println();
+ if (hasFlexibilityConstraint()) {
+ pw.print("Num Required Flexible constraints: ");
+ pw.print(getNumRequiredFlexibleConstraints());
+ pw.println();
+ pw.print("Num Dropped Flexible constraints: ");
+ pw.print(getNumDroppedFlexibleConstraints());
+ pw.println();
+ }
pw.println("Constraint history:");
pw.increaseIndent();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index d4a1cd234c39..4fe021a35f98 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -721,25 +721,28 @@ class Agent {
@GuardedBy("mLock")
void distributeBasicIncomeLocked(int batteryLevel) {
- final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages();
+ final SparseArrayMap<String, InstalledPackageInfo> pkgs = mIrs.getInstalledPackages();
final long now = getCurrentTimeMillis();
- for (int i = 0; i < pkgs.size(); ++i) {
- final InstalledPackageInfo pkgInfo = pkgs.get(i);
- if (!shouldGiveCredits(pkgInfo)) {
- continue;
- }
- final int userId = UserHandle.getUserId(pkgInfo.uid);
- final String pkgName = pkgInfo.packageName;
- final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
- final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
- final double perc = batteryLevel / 100d;
- // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
- final long shortfall = minBalance - ledger.getCurrentBalance();
- if (shortfall > 0) {
- recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
- null, (long) (perc * shortfall), 0), true);
+ for (int uIdx = pkgs.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = pkgs.keyAt(uIdx);
+
+ for (int pIdx = pkgs.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
+ final InstalledPackageInfo pkgInfo = pkgs.valueAt(uIdx, pIdx);
+ if (!shouldGiveCredits(pkgInfo)) {
+ continue;
+ }
+ final String pkgName = pkgInfo.packageName;
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ final double perc = batteryLevel / 100d;
+ // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
+ final long shortfall = minBalance - ledger.getCurrentBalance();
+ if (shortfall > 0) {
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
+ null, (long) (perc * shortfall), 0), true);
+ }
}
}
}
@@ -1325,7 +1328,6 @@ class Agent {
@GuardedBy("mLock")
void dumpLocked(IndentingPrintWriter pw) {
- pw.println();
mBalanceThresholdAlarmQueue.dump(pw);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 2fb0c1a36e07..22a2f5163538 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -405,6 +405,10 @@ public abstract class EconomicPolicy {
return "PROMOTION";
case REGULATION_DEMOTION:
return "DEMOTION";
+ case REGULATION_BG_RESTRICTED:
+ return "BG_RESTRICTED";
+ case REGULATION_BG_UNRESTRICTED:
+ return "BG_UNRESTRICTED";
}
return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 2b8272208c3e..448a808ff52f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -136,7 +136,7 @@ public class InternalResourceService extends SystemService {
@NonNull
@GuardedBy("mLock")
- private final List<InstalledPackageInfo> mPkgCache = new ArrayList<>();
+ private final SparseArrayMap<String, InstalledPackageInfo> mPkgCache = new SparseArrayMap<>();
/** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
@GuardedBy("mLock")
@@ -343,7 +343,7 @@ public class InternalResourceService extends SystemService {
}
@NonNull
- List<InstalledPackageInfo> getInstalledPackages() {
+ SparseArrayMap<String, InstalledPackageInfo> getInstalledPackages() {
synchronized (mLock) {
return mPkgCache;
}
@@ -354,11 +354,13 @@ public class InternalResourceService extends SystemService {
List<InstalledPackageInfo> getInstalledPackages(final int userId) {
final List<InstalledPackageInfo> userPkgs = new ArrayList<>();
synchronized (mLock) {
- for (int i = 0; i < mPkgCache.size(); ++i) {
- final InstalledPackageInfo packageInfo = mPkgCache.get(i);
- if (UserHandle.getUserId(packageInfo.uid) == userId) {
- userPkgs.add(packageInfo);
- }
+ final int uIdx = mPkgCache.indexOfKey(userId);
+ if (uIdx < 0) {
+ return userPkgs;
+ }
+ for (int p = mPkgCache.numElementsForKeyAt(uIdx) - 1; p >= 0; --p) {
+ final InstalledPackageInfo packageInfo = mPkgCache.valueAt(uIdx, p);
+ userPkgs.add(packageInfo);
}
}
return userPkgs;
@@ -511,7 +513,7 @@ public class InternalResourceService extends SystemService {
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- mPkgCache.add(new InstalledPackageInfo(packageInfo));
+ mPkgCache.add(userId, pkgName, new InstalledPackageInfo(packageInfo));
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
@@ -532,14 +534,7 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mUidToPackageCache.remove(uid, pkgName);
mVipOverrides.delete(userId, pkgName);
- for (int i = 0; i < mPkgCache.size(); ++i) {
- final InstalledPackageInfo pkgInfo = mPkgCache.get(i);
- if (UserHandle.getUserId(pkgInfo.uid) == userId
- && pkgName.equals(pkgInfo.packageName)) {
- mPkgCache.remove(i);
- break;
- }
- }
+ mPkgCache.delete(userId, pkgName);
mAgent.onPackageRemovedLocked(userId, pkgName);
}
}
@@ -560,7 +555,8 @@ public class InternalResourceService extends SystemService {
final List<PackageInfo> pkgs =
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
- mPkgCache.add(new InstalledPackageInfo(pkgs.get(i)));
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
+ mPkgCache.add(userId, ipo.packageName, ipo);
}
mAgent.grantBirthrightsLocked(userId);
}
@@ -570,15 +566,15 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mVipOverrides.delete(userId);
ArrayList<String> removedPkgs = new ArrayList<>();
- for (int i = mPkgCache.size() - 1; i >= 0; --i) {
- final InstalledPackageInfo pkgInfo = mPkgCache.get(i);
- if (UserHandle.getUserId(pkgInfo.uid) == userId) {
+ final int uIdx = mPkgCache.indexOfKey(userId);
+ if (uIdx >= 0) {
+ for (int p = mPkgCache.numElementsForKeyAt(uIdx) - 1; p >= 0; --p) {
+ final InstalledPackageInfo pkgInfo = mPkgCache.valueAt(uIdx, p);
removedPkgs.add(pkgInfo.packageName);
mUidToPackageCache.remove(pkgInfo.uid);
- mPkgCache.remove(i);
- break;
}
}
+ mPkgCache.delete(userId);
mAgent.onUserRemovedLocked(userId, removedPkgs);
}
}
@@ -727,7 +723,8 @@ public class InternalResourceService extends SystemService {
final List<PackageInfo> pkgs =
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
- mPkgCache.add(new InstalledPackageInfo(pkgs.get(i)));
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
+ mPkgCache.add(userId, ipo.packageName, ipo);
}
}
}
@@ -1185,7 +1182,6 @@ public class InternalResourceService extends SystemService {
// User setting should override DeviceConfig setting.
// NOTE: There's currently no way for a user to reset the value (via UI), so if a user
// manually toggles TARE via UI, we'll always defer to the user's current setting
- // TODO: add a "reset" value if the user toggle is an issue
final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1);
final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index e91ed1287e1b..620d1a0da76f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -247,7 +247,7 @@ class Ledger {
boolean printedTransactionTitle = false;
for (int t = 0; t < Math.min(MAX_TRANSACTION_COUNT, numRecentTransactions); ++t) {
- final int idx = (mTransactionIndex - t + MAX_TRANSACTION_COUNT) % MAX_TRANSACTION_COUNT;
+ final int idx = (mTransactionIndex + t) % MAX_TRANSACTION_COUNT;
final Transaction transaction = mTransactions[idx];
if (transaction == null) {
continue;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 2cae83f4aad5..29478d05e066 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -216,17 +216,21 @@ public class Scribe {
mRemainingConsumableCakes = 0;
final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
- final List<InstalledPackageInfo> installedPackages = mIrs.getInstalledPackages();
- for (int i = 0; i < installedPackages.size(); ++i) {
- final InstalledPackageInfo packageInfo = installedPackages.get(i);
- if (packageInfo.uid != InstalledPackageInfo.NO_UID) {
- final int userId = UserHandle.getUserId(packageInfo.uid);
- ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId);
- if (pkgsForUser == null) {
- pkgsForUser = new ArraySet<>();
- installedPackagesPerUser.put(userId, pkgsForUser);
+ final SparseArrayMap<String, InstalledPackageInfo> installedPackages =
+ mIrs.getInstalledPackages();
+ for (int uIdx = installedPackages.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = installedPackages.keyAt(uIdx);
+
+ for (int pIdx = installedPackages.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
+ final InstalledPackageInfo packageInfo = installedPackages.valueAt(uIdx, pIdx);
+ if (packageInfo.uid != InstalledPackageInfo.NO_UID) {
+ ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId);
+ if (pkgsForUser == null) {
+ pkgsForUser = new ArraySet<>();
+ installedPackagesPerUser.put(userId, pkgsForUser);
+ }
+ pkgsForUser.add(packageInfo.packageName);
}
- pkgsForUser.add(packageInfo.packageName);
}
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 073d987f5dad..083bbf01bf5b 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -235,9 +235,11 @@ Status Idmap2Service::createFabricatedOverlay(
for (const auto& res : overlay.entries) {
if (res.dataType == Res_value::TYPE_STRING) {
- builder.SetResourceValue(res.resourceName, res.dataType, res.stringData.value());
+ builder.SetResourceValue(res.resourceName, res.dataType, res.stringData.value(),
+ res.configuration.value_or(std::string()));
} else {
- builder.SetResourceValue(res.resourceName, res.dataType, res.data);
+ builder.SetResourceValue(res.resourceName, res.dataType, res.data,
+ res.configuration.value_or(std::string()));
}
}
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
index a6824da8c424..c773e112997d 100644
--- a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
@@ -24,4 +24,5 @@ parcelable FabricatedOverlayInternalEntry {
int dataType;
int data;
@nullable @utf8InCpp String stringData;
+ @nullable @utf8InCpp String configuration;
} \ No newline at end of file
diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
index 2fc4d43b798a..05b0618131c9 100644
--- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h
+++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
@@ -39,10 +39,11 @@ struct FabricatedOverlay {
Builder& SetOverlayable(const std::string& name);
Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type,
- uint32_t data_value);
+ uint32_t data_value, const std::string& configuration);
Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type,
- const std::string& data_string_value);
+ const std::string& data_string_value,
+ const std::string& configuration);
WARN_UNUSED Result<FabricatedOverlay> Build();
@@ -52,6 +53,7 @@ struct FabricatedOverlay {
DataType data_type;
DataValue data_value;
std::string data_string_value;
+ std::string configuration;
};
std::string package_name_;
diff --git a/cmds/idmap2/include/idmap2/ResourceContainer.h b/cmds/idmap2/include/idmap2/ResourceContainer.h
index c3ba4640bd77..2452ff0784ce 100644
--- a/cmds/idmap2/include/idmap2/ResourceContainer.h
+++ b/cmds/idmap2/include/idmap2/ResourceContainer.h
@@ -66,7 +66,7 @@ struct OverlayData {
struct Value {
std::string resource_name;
- std::variant<ResourceIdValue, TargetValue> value;
+ std::variant<ResourceIdValue, TargetValueWithConfig> value;
};
struct InlineStringPoolData {
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 5a0a384f75a3..21862a3635d5 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -69,7 +69,8 @@ class ResourceMapping {
// If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target
// resource id is mapped to an overlay resource id.
Result<Unit> AddMapping(ResourceId target_resource,
- const std::variant<OverlayData::ResourceIdValue, TargetValue>& value);
+ const std::variant<OverlayData::ResourceIdValue,
+ TargetValueWithConfig>& value);
TargetResourceMap target_map_;
OverlayResourceMap overlay_map_;
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 414aa064ada7..8ec749602c4a 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -43,6 +43,11 @@ struct TargetValue {
std::string data_string_value;
};
+struct TargetValueWithConfig {
+ TargetValue value;
+ std::string config;
+};
+
namespace utils {
// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
index 5bbe08524c18..bde9b0be4361 100644
--- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
+++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
@@ -70,19 +70,22 @@ FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
- const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
- entries_.emplace_back(Entry{resource_name, data_type, data_value, ""});
+ const std::string& resource_name, uint8_t data_type, uint32_t data_value,
+ const std::string& configuration) {
+ entries_.emplace_back(Entry{resource_name, data_type, data_value, "", configuration});
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
- const std::string& resource_name, uint8_t data_type, const std::string& data_string_value) {
- entries_.emplace_back(Entry{resource_name, data_type, 0, data_string_value});
+ const std::string& resource_name, uint8_t data_type, const std::string& data_string_value,
+ const std::string& configuration) {
+ entries_.emplace_back(Entry{resource_name, data_type, 0, data_string_value, configuration});
return *this;
}
Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
- using EntryMap = std::map<std::string, TargetValue>;
+ using ConfigMap = std::map<std::string, TargetValue>;
+ using EntryMap = std::map<std::string, ConfigMap>;
using TypeMap = std::map<std::string, EntryMap>;
using PackageMap = std::map<std::string, TypeMap>;
PackageMap package_map;
@@ -123,11 +126,16 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
auto entry = type->second.find(entry_name.to_string());
if (entry == type->second.end()) {
- entry = type->second.insert(std::make_pair(entry_name.to_string(), TargetValue())).first;
+ entry = type->second.insert(std::make_pair(entry_name.to_string(), ConfigMap())).first;
}
- entry->second = TargetValue{
- res_entry.data_type, res_entry.data_value, res_entry.data_string_value};
+ auto value = entry->second.find(res_entry.configuration);
+ if (value == entry->second.end()) {
+ value = entry->second.insert(std::make_pair(res_entry.configuration, TargetValue())).first;
+ }
+
+ value->second = TargetValue{res_entry.data_type, res_entry.data_value,
+ res_entry.data_string_value};
}
pb::FabricatedOverlay overlay_pb;
@@ -145,15 +153,18 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
type_pb->set_name(type.first);
for (auto& entry : type.second) {
- auto entry_pb = type_pb->add_entries();
- entry_pb->set_name(entry.first);
- pb::ResourceValue* value = entry_pb->mutable_res_value();
- value->set_data_type(entry.second.data_type);
- if (entry.second.data_type == Res_value::TYPE_STRING) {
- auto ref = string_pool.MakeRef(entry.second.data_string_value);
- value->set_data_value(ref.index());
- } else {
- value->set_data_value(entry.second.data_value);
+ for (const auto& value: entry.second) {
+ auto entry_pb = type_pb->add_entries();
+ entry_pb->set_name(entry.first);
+ entry_pb->set_configuration(value.first);
+ pb::ResourceValue* pb_value = entry_pb->mutable_res_value();
+ pb_value->set_data_type(value.second.data_type);
+ if (value.second.data_type == Res_value::TYPE_STRING) {
+ auto ref = string_pool.MakeRef(value.second.data_string_value);
+ pb_value->set_data_value(ref.index());
+ } else {
+ pb_value->set_data_value(value.second.data_value);
+ }
}
}
}
@@ -330,8 +341,9 @@ Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info
entry.name().c_str());
const auto& res_value = entry.res_value();
result.pairs.emplace_back(OverlayData::Value{
- name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
- .data_value = res_value.data_value()}});
+ name, TargetValueWithConfig{.config = entry.configuration(), .value = TargetValue{
+ .data_type = static_cast<uint8_t>(res_value.data_type()),
+ .data_value = res_value.data_value()}}});
}
}
}
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index a62472c8f11e..0e3590486c6f 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -226,8 +226,10 @@ Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider
*target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}});
} else {
overlay_data.pairs.emplace_back(
- OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType,
- .data_value = overlay_resource->data}});
+ OverlayData::Value{*target_resource, TargetValueWithConfig{
+ .config = std::string(),
+ .value = TargetValue{.data_type = overlay_resource->dataType,
+ .data_value = overlay_resource->data}}});
}
}
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 3bbbf248c87d..8ebe5aa46e73 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -160,7 +160,7 @@ Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceCont
Result<Unit> ResourceMapping::AddMapping(
ResourceId target_resource,
- const std::variant<OverlayData::ResourceIdValue, TargetValue>& value) {
+ const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
@@ -176,8 +176,8 @@ Result<Unit> ResourceMapping::AddMapping(
overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
}
} else {
- auto overlay_value = std::get<TargetValue>(value);
- target_map_.insert(std::make_pair(target_resource, overlay_value));
+ auto overlay_value = std::get<TargetValueWithConfig>(value);
+ target_map_.insert(std::make_pair(target_resource, overlay_value.value));
}
return Unit{};
diff --git a/cmds/idmap2/libidmap2/proto/fabricated_v1.proto b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto
index a392b2b6d856..c7a79b31e151 100644
--- a/cmds/idmap2/libidmap2/proto/fabricated_v1.proto
+++ b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto
@@ -46,6 +46,7 @@ message ResourceEntry {
oneof value {
ResourceValue res_value = 2;
}
+ string configuration = 3;
}
message ResourceValue {
diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
index 91331ca1cc76..e804c879ee82 100644
--- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -43,10 +43,17 @@ TEST(FabricatedOverlayTests, OverlayInfo) {
TEST(FabricatedOverlayTests, SetResourceValue) {
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
- .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
- .SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U)
- .SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000)
- .SetResourceValue("com.example.target:string/string1", Res_value::TYPE_STRING, "foobar")
+ .SetResourceValue(
+ "com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U, "port")
+ .SetResourceValue(
+ "com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U, "land")
+ .SetResourceValue(
+ "string/int3", Res_value::TYPE_REFERENCE, 0x7f010000, "xxhdpi-v7")
+ .SetResourceValue(
+ "com.example.target:string/string1",
+ Res_value::TYPE_STRING,
+ "foobar",
+ "en-rUS-normal-xxhdpi-v21")
.Build();
ASSERT_TRUE(overlay);
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
@@ -66,44 +73,48 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
auto& it = pairs->pairs[0];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
- auto entry = std::get_if<TargetValue>(&it.value);
+ auto entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- ASSERT_EQ(1U, entry->data_value);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+ ASSERT_EQ(1U, entry->value.data_value);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->value.data_type);
+ ASSERT_EQ("port", entry->config);
it = pairs->pairs[1];
ASSERT_EQ("com.example.target:string/int3", it.resource_name);
- entry = std::get_if<TargetValue>(&it.value);
+ entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- ASSERT_EQ(0x7f010000, entry->data_value);
- ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type);
+ ASSERT_EQ(0x7f010000, entry->value.data_value);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->value.data_type);
+ ASSERT_EQ("xxhdpi-v7", entry->config);
it = pairs->pairs[2];
ASSERT_EQ("com.example.target:string/string1", it.resource_name);
- entry = std::get_if<TargetValue>(&it.value);
+ entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- ASSERT_EQ(Res_value::TYPE_STRING, entry->data_type);
- ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->data_value).value_or(""));
+ ASSERT_EQ(Res_value::TYPE_STRING, entry->value.data_type);
+ ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->value.data_value).value_or(""));
+ ASSERT_EQ("en-rUS-normal-xxhdpi-v21", entry->config);
it = pairs->pairs[3];
ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
- entry = std::get_if<TargetValue>(&it.value);
+ entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- ASSERT_EQ(2U, entry->data_value);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+ ASSERT_EQ(2U, entry->value.data_value);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->value.data_type);
+ ASSERT_EQ("land", entry->config);
}
TEST(FabricatedOverlayTests, SetResourceValueBadArgs) {
{
auto builder =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
- .SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U);
+ .SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U, "");
ASSERT_FALSE(builder.Build());
}
{
auto builder =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
- .SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U);
+ .SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U, "");
ASSERT_FALSE(builder.Build());
}
}
@@ -112,8 +123,9 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetOverlayable("TestResources")
- .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
- .SetResourceValue("com.example.target:string/string1", Res_value::TYPE_STRING, "foobar")
+ .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U, "")
+ .SetResourceValue(
+ "com.example.target:string/string1", Res_value::TYPE_STRING, "foobar", "")
.Build();
ASSERT_TRUE(overlay);
TemporaryFile tf;
@@ -142,17 +154,17 @@ TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
auto& it = pairs->pairs[0];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
- auto entry = std::get_if<TargetValue>(&it.value);
+ auto entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- EXPECT_EQ(1U, entry->data_value);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+ EXPECT_EQ(1U, entry->value.data_value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->value.data_type);
it = pairs->pairs[1];
ASSERT_EQ("com.example.target:string/string1", it.resource_name);
- entry = std::get_if<TargetValue>(&it.value);
+ entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
- ASSERT_EQ(Res_value::TYPE_STRING, entry->data_type);
- ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->data_value).value_or(""));
+ ASSERT_EQ(Res_value::TYPE_STRING, entry->value.data_type);
+ ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->value.data_value).value_or(""));
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index a3799f98c90c..ee9a424b3050 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -261,9 +261,9 @@ TEST(IdmapTests, FabricatedOverlay) {
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
- .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
- .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
- .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar")
+ .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
+ .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
+ .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
.Build();
ASSERT_TRUE(frro);
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index c05abcf31532..ca9a444bcb05 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -194,9 +194,9 @@ TEST(ResourceMappingTests, InlineResources) {
TEST(ResourceMappingTests, FabricatedOverlay) {
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
- .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
- .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
- .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar")
+ .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
+ .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
+ .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
.Build();
ASSERT_TRUE(frro);
diff --git a/core/api/current.txt b/core/api/current.txt
index 795c43056d04..eace67b79cd5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32264,6 +32264,7 @@ package android.os {
method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}) public java.util.List<android.os.UserHandle> getVisibleUsers();
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
method public static boolean isHeadlessSystemUserMode();
@@ -49971,6 +49972,7 @@ package android.view {
method public void invalidate();
method public void invalidateDrawable(@NonNull android.graphics.drawable.Drawable);
method public void invalidateOutline();
+ method public boolean isAccessibilityDataPrivate();
method public boolean isAccessibilityFocused();
method public boolean isAccessibilityHeading();
method public boolean isActivated();
@@ -50148,6 +50150,7 @@ package android.view {
method public void scrollTo(int, int);
method public void sendAccessibilityEvent(int);
method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent);
+ method public void setAccessibilityDataPrivate(int);
method public void setAccessibilityDelegate(@Nullable android.view.View.AccessibilityDelegate);
method public void setAccessibilityHeading(boolean);
method public void setAccessibilityLiveRegion(int);
@@ -50328,6 +50331,9 @@ package android.view {
method @CallSuper protected boolean verifyDrawable(@NonNull android.graphics.drawable.Drawable);
method @Deprecated public boolean willNotCacheDrawing();
method public boolean willNotDraw();
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0; // 0x0
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 2; // 0x2
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 1; // 0x1
field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
@@ -51780,9 +51786,11 @@ package android.view.accessibility {
method public int getSpeechStateChangeTypes();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
+ method public boolean isAccessibilityDataPrivate();
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain();
+ method public void setAccessibilityDataPrivate(boolean);
method public void setAction(int);
method public void setContentChangeTypes(int);
method public void setEventTime(long);
@@ -51873,6 +51881,7 @@ package android.view.accessibility {
method public static boolean isAccessibilityButtonSupported();
method public boolean isAudioDescriptionRequested();
method public boolean isEnabled();
+ method public boolean isRequestFromAccessibilityTool();
method public boolean isTouchExplorationEnabled();
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a0cc4f0d6775..2b2f2023bef6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -320,6 +320,7 @@ package android {
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
+ field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -507,7 +508,7 @@ package android.app {
public class ActivityOptions {
method public int getLaunchTaskId();
- method @RequiresPermission("android.permission.START_TASKS_FROM_RECENTS") public void setLaunchTaskId(int);
+ method @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public void setLaunchTaskId(int);
}
public class AlarmManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fefdfd8d7b15..4e594c1741fe 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -42,7 +42,6 @@ package android {
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
field public static final String SET_GAME_SERVICE = "android.permission.SET_GAME_SERVICE";
field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
- field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -90,6 +89,7 @@ package android.accessibilityservice {
public class AccessibilityServiceInfo implements android.os.Parcelable {
method @NonNull public android.content.ComponentName getComponentName();
+ method public void setAccessibilityTool(boolean);
}
}
@@ -3204,6 +3204,11 @@ package android.widget {
method public android.widget.ListView getMenuListView();
}
+ public class RatingBar extends android.widget.AbsSeekBar {
+ field public static final String PLURALS_MAX = "max";
+ field public static final String PLURALS_RATING = "rating";
+ }
+
public class Spinner extends android.widget.AbsSpinner implements android.content.DialogInterface.OnClickListener {
method public boolean isPopupShowing();
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 2e89ce83cd36..8f6bfd3b13db 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -784,6 +784,7 @@ public class AccessibilityServiceInfo implements Parcelable {
mNonInteractiveUiTimeout = other.mNonInteractiveUiTimeout;
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
+ mIsAccessibilityTool = other.mIsAccessibilityTool;
}
private boolean isRequestAccessibilityButtonChangeEnabled(IPlatformCompat platformCompat) {
@@ -1112,6 +1113,26 @@ public class AccessibilityServiceInfo implements Parcelable {
}
/**
+ * Sets whether the service is used to assist users with disabilities.
+ *
+ * <p>
+ * This property is normally provided in the service's {@link #mResolveInfo ResolveInfo}.
+ * </p>
+ *
+ * <p>
+ * This method is helpful for unit testing. However, this property is not dynamically
+ * configurable by a standard {@link AccessibilityService} so it's not possible to update the
+ * copy held by the system with this method.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ public void setAccessibilityTool(boolean isAccessibilityTool) {
+ mIsAccessibilityTool = isAccessibilityTool;
+ }
+
+ /**
* Indicates if the service is used to assist users with disabilities.
*
* @return {@code true} if the property is set to true.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d6b90a2792a9..2a5916dfb6a8 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1906,7 +1906,6 @@ public class AppOpsManager {
OP_SCHEDULE_EXACT_ALARM,
OP_MANAGE_MEDIA,
OP_TURN_SCREEN_ON,
- OP_GET_USAGE_STATS,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 807bd5764cba..2b245aae915f 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -20,7 +20,6 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -68,7 +67,7 @@ public final class NotificationChannelGroup implements Parcelable {
private CharSequence mName;
private String mDescription;
private boolean mBlocked;
- private ParceledListSlice<NotificationChannel> mChannels;
+ private List<NotificationChannel> mChannels = new ArrayList<>();
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -103,8 +102,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mDescription = null;
}
- mChannels = in.readParcelable(
- NotificationChannelGroup.class.getClassLoader(), ParceledListSlice.class);
+ in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mBlocked = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -131,7 +129,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
dest.writeByte((byte) 0);
}
- dest.writeParcelable(mChannels, flags);
+ dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
dest.writeInt(mUserLockedFields);
}
@@ -161,7 +159,7 @@ public final class NotificationChannelGroup implements Parcelable {
* Returns the list of channels that belong to this group
*/
public List<NotificationChannel> getChannels() {
- return mChannels == null ? new ArrayList<>() : mChannels.getList();
+ return mChannels;
}
/**
@@ -195,8 +193,15 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
+ public void addChannel(NotificationChannel channel) {
+ mChannels.add(channel);
+ }
+
+ /**
+ * @hide
+ */
public void setChannels(List<NotificationChannel> channels) {
- mChannels = new ParceledListSlice<>(channels);
+ mChannels = channels;
}
/**
@@ -331,7 +336,7 @@ public final class NotificationChannelGroup implements Parcelable {
proto.write(NotificationChannelGroupProto.NAME, mName.toString());
proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
- for (NotificationChannel channel : mChannels.getList()) {
+ for (NotificationChannel channel : mChannels) {
channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS);
}
proto.end(token);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index a045157e02db..8d57e32a763c 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -550,6 +550,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
+ info.setAccessibilityTool(true);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 8ffc6a28bef3..c1a3c1934dbb 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1008,6 +1008,22 @@ public final class CompanionDeviceManager {
}
}
+ /**
+ * Checks whether the calling companion application is currently bound.
+ *
+ * @return true if application is bound, false otherwise
+ * @hide
+ */
+ @UserHandleAware
+ public boolean isCompanionApplicationBound() {
+ try {
+ return mService.isCompanionApplicationBound(
+ mContext.getOpPackageName(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private boolean checkFeaturePresent() {
boolean featurePresent = mService != null;
if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 6e6e18765639..17e313252010 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -80,4 +80,6 @@ interface ICompanionDeviceManager {
void attachSystemDataTransport(String packageName, int userId, int associationId, in ParcelFileDescriptor fd);
void detachSystemDataTransport(String packageName, int userId, int associationId);
+
+ boolean isCompanionApplicationBound(String packageName, int userId);
}
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
index 5efc1f9f7cca..3ca056097c1f 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -112,6 +112,28 @@ public class FabricatedOverlay {
* @param resourceName name of the target resource to overlay (in the form
* [package]:type/entry)
* @param dataType the data type of the new value
+ * @param value the unsigned 32 bit integer representing the new value
+ * @param configuration The string representation of the config this overlay is enabled for
+ *
+ * @see android.util.TypedValue#type
+ */
+ public Builder setResourceValue(@NonNull String resourceName, int dataType, int value,
+ String configuration) {
+ final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+ entry.resourceName = resourceName;
+ entry.dataType = dataType;
+ entry.data = value;
+ entry.configuration = configuration;
+ mEntries.add(entry);
+ return this;
+ }
+
+ /**
+ * Sets the value of the fabricated overlay
+ *
+ * @param resourceName name of the target resource to overlay (in the form
+ * [package]:type/entry)
+ * @param dataType the data type of the new value
* @param value the string representing the new value
*
* @see android.util.TypedValue#type
@@ -125,6 +147,28 @@ public class FabricatedOverlay {
return this;
}
+ /**
+ * Sets the value of the fabricated overlay
+ *
+ * @param resourceName name of the target resource to overlay (in the form
+ * [package]:type/entry)
+ * @param dataType the data type of the new value
+ * @param value the string representing the new value
+ * @param configuration The string representation of the config this overlay is enabled for
+ *
+ * @see android.util.TypedValue#type
+ */
+ public Builder setResourceValue(@NonNull String resourceName, int dataType, String value,
+ String configuration) {
+ final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+ entry.resourceName = resourceName;
+ entry.dataType = dataType;
+ entry.stringData = value;
+ entry.configuration = configuration;
+ mEntries.add(entry);
+ return this;
+ }
+
/** Builds an immutable fabricated overlay. */
public FabricatedOverlay build() {
final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5839b877a448..8e2a5eaff2d2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8065,7 +8065,7 @@ public abstract class PackageManager {
}
PackageParser.Package pkg = parser.parsePackage(apkFile, 0, false);
- if ((flagsBits & GET_SIGNATURES) != 0) {
+ if ((flagsBits & GET_SIGNATURES) != 0 || (flagsBits & GET_SIGNING_CERTIFICATES) != 0) {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 3d593872757d..05daf63fdd5d 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -81,6 +81,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_INIT_INK_WINDOW = 120;
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
private static final int DO_UPDATE_TOOL_TYPE = 140;
+ private static final int DO_REMOVE_STYLUS_HANDWRITING_WINDOW = 150;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -254,6 +255,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputMethod.finishStylusHandwriting();
return;
}
+ case DO_REMOVE_STYLUS_HANDWRITING_WINDOW: {
+ inputMethod.removeStylusHandwritingWindow();
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
@@ -434,4 +439,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void finishStylusHandwriting() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_STYLUS_HANDWRITING));
}
+
+ @BinderThread
+ @Override
+ public void removeStylusHandwritingWindow() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW));
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index c2027b136f0e..48b9b8869cae 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -621,7 +621,7 @@ public class InputMethodService extends AbstractInputMethodService {
private boolean mDestroyed;
private boolean mOnPreparedStylusHwCalled;
- /** Stylus handwriting Ink window. */
+ /** Stylus handwriting Ink window. */
private InkWindow mInkWindow;
/**
@@ -708,9 +708,6 @@ public class InputMethodService extends AbstractInputMethodService {
mConfigTracker.onInitialize(params.configChanges);
mPrivOps.set(params.privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(params.token, mPrivOps);
- if (params.stylusHandWritingSupported) {
- mInkWindow = new InkWindow(mWindow.getContext());
- }
mNavigationBarController.onNavButtonFlagsChanged(params.navigationBarFlags);
attachToken(params.token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -744,9 +741,6 @@ public class InputMethodService extends AbstractInputMethodService {
attachToWindowToken(token);
mToken = token;
mWindow.setToken(token);
- if (mInkWindow != null) {
- mInkWindow.setToken(token);
- }
}
/**
@@ -785,7 +779,7 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = null;
// free-up cached InkWindow surface on detaching from current client.
if (mInkWindow != null) {
- mInkWindow.hide(true /* remove */);
+ removeHandwritingInkWindow();
}
}
@@ -972,6 +966,7 @@ public class InputMethodService extends AbstractInputMethodService {
Log.d(TAG, "Input should have started before starting Stylus handwriting.");
return;
}
+ maybeCreateInkWindow();
if (!mOnPreparedStylusHwCalled) {
// prepare hasn't been called by Stylus HOVER.
onPrepareStylusHandwriting();
@@ -1031,12 +1026,24 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@Override
public void initInkWindow() {
+ maybeCreateInkWindow();
mInkWindow.initOnly();
onPrepareStylusHandwriting();
mOnPreparedStylusHwCalled = true;
}
/**
+ * Create and attach token to Ink window if it wasn't already created.
+ */
+ private void maybeCreateInkWindow() {
+ if (mInkWindow == null) {
+ mInkWindow = new InkWindow(mWindow.getContext());
+ mInkWindow.setToken(mToken);
+ }
+ // TODO(b/243571274): set an idle-timeout after which InkWindow is removed.
+ }
+
+ /**
* {@inheritDoc}
* @hide
*/
@@ -1047,6 +1054,15 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void removeStylusHandwritingWindow() {
+ InputMethodService.this.removeStylusHandwritingWindow();
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
@@ -2511,6 +2527,7 @@ public class InputMethodService extends AbstractInputMethodService {
mHandwritingEventReceiver.dispose();
mHandwritingEventReceiver = null;
+ // TODO(b/243571274): set an idle-timeout after which InkWindow is removed.
mInkWindow.hide(false /* remove */);
mPrivOps.resetStylusHandwriting(requestId);
@@ -2519,6 +2536,27 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Remove Stylus handwriting window.
+ * Typically, this is called when {@link InkWindow} should no longer be holding a surface in
+ * memory.
+ */
+ private void removeStylusHandwritingWindow() {
+ if (mInkWindow != null) {
+ if (mHandwritingRequestId.isPresent()) {
+ // if handwriting session is still ongoing. This shouldn't happen.
+ finishStylusHandwriting();
+ }
+ removeHandwritingInkWindow();
+ }
+ }
+
+ private void removeHandwritingInkWindow() {
+ mInkWindow.hide(true /* remove */);
+ mInkWindow.destroy();
+ mInkWindow = null;
+ }
+
+ /**
* Sets the duration after which an ongoing stylus handwriting session that hasn't received new
* {@link MotionEvent}s will time out and {@link #finishStylusHandwriting()} will be called.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9d05cec06d01..da206268917e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -955,7 +955,16 @@ public abstract class BatteryStats {
public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5;
- public static final int NUM_USER_ACTIVITY_TYPES = PowerManager.USER_ACTIVITY_EVENT_MAX + 1;
+ /**
+ * Note that these must match the constants in android.os.PowerManager.
+ * Also, if the user activity types change, the BatteryStatsImpl.VERSION must
+ * also be bumped.
+ */
+ static final String[] USER_ACTIVITY_TYPES = {
+ "other", "button", "touch", "accessibility", "attention"
+ };
+
+ public static final int NUM_USER_ACTIVITY_TYPES = USER_ACTIVITY_TYPES.length;
public abstract void noteUserActivityLocked(int type);
public abstract boolean hasUserActivity();
@@ -2317,11 +2326,6 @@ public abstract class BatteryStats {
public abstract void finishIteratingHistoryLocked();
/**
- * Return the base time offset for the battery history.
- */
- public abstract long getHistoryBaseTime();
-
- /**
* Returns the number of times the device has been started.
*/
public abstract int getStartCount();
@@ -6159,7 +6163,7 @@ public abstract class BatteryStats {
}
sb.append(val);
sb.append(" ");
- sb.append(PowerManager.userActivityEventToString(i));
+ sb.append(Uid.USER_ACTIVITY_TYPES[i]);
}
}
if (hasData) {
@@ -7606,8 +7610,6 @@ public abstract class BatteryStats {
CHECKIN_VERSION, getParcelVersion(), getStartPlatformVersion(),
getEndPlatformVersion());
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
if (startIteratingHistoryLocked()) {
try {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index fa6b1189f1da..88649cbf9e42 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -129,6 +129,7 @@ interface IUserManager {
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
boolean isUserVisible(int userId);
+ List<UserHandle> getVisibleUsers();
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a3a3e3f80f3d..01b75d104e5e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -348,44 +348,6 @@ public final class PowerManager {
public static final int USER_ACTIVITY_EVENT_DEVICE_STATE = 6;
/**
- * @hide
- */
- public static final int USER_ACTIVITY_EVENT_MAX = USER_ACTIVITY_EVENT_DEVICE_STATE;
-
- /**
- * @hide
- */
- @IntDef(prefix = { "USER_ACTIVITY_EVENT_" }, value = {
- USER_ACTIVITY_EVENT_OTHER,
- USER_ACTIVITY_EVENT_BUTTON,
- USER_ACTIVITY_EVENT_TOUCH,
- USER_ACTIVITY_EVENT_ACCESSIBILITY,
- USER_ACTIVITY_EVENT_ATTENTION,
- USER_ACTIVITY_EVENT_FACE_DOWN,
- USER_ACTIVITY_EVENT_DEVICE_STATE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface UserActivityEvent{}
-
- /**
- *
- * Convert the user activity event to a string for debugging purposes.
- * @hide
- */
- public static String userActivityEventToString(@UserActivityEvent int userActivityEvent) {
- switch (userActivityEvent) {
- case USER_ACTIVITY_EVENT_OTHER: return "other";
- case USER_ACTIVITY_EVENT_BUTTON: return "button";
- case USER_ACTIVITY_EVENT_TOUCH: return "touch";
- case USER_ACTIVITY_EVENT_ACCESSIBILITY: return "accessibility";
- case USER_ACTIVITY_EVENT_ATTENTION: return "attention";
- case USER_ACTIVITY_EVENT_FACE_DOWN: return "faceDown";
- case USER_ACTIVITY_EVENT_DEVICE_STATE: return "deviceState";
- default: return Integer.toString(userActivityEvent);
- }
- }
-
- /**
* User activity flag: If already dimmed, extend the dim timeout
* but do not brighten. This flag is useful for keeping the screen on
* a little longer without causing a visible change such as when
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 738f9c9c4f13..f6aaee8e3de0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2886,6 +2886,21 @@ public class UserManager {
}
/**
+ * Gets the visible users (as defined by {@link #isUserVisible()}.
+ *
+ * @return visible users at the moment.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS})
+ public @NonNull List<UserHandle> getVisibleUsers() {
+ try {
+ return mService.getVisibleUsers();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the context user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8a50e79e8598..14598d558caa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9187,14 +9187,12 @@ public final class Settings {
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
- * The complications that are enabled to be shown over the screensaver by the user. Holds
- * a comma separated list of
- * {@link com.android.settingslib.dream.DreamBackend.ComplicationType}.
+ * Whether complications are enabled to be shown over the screensaver by the user.
*
* @hide
*/
- public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
- "screensaver_enabled_complications";
+ public static final String SCREENSAVER_COMPLICATIONS_ENABLED =
+ "screensaver_complications_enabled";
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0338cebf75c6..bca100a1d81e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -52,13 +52,6 @@ public class FeatureFlagUtils {
public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
/**
- * Support per app's language selection
- * @hide
- */
- public static final String SETTINGS_APP_LANGUAGE_SELECTION = "settings_app_language_selection";
-
-
- /**
* Feature flag to allow/restrict intent redirection from/to clone profile.
* Default value is false,this is to ensure that framework is not impacted by intent redirection
* till we are ready to launch.
@@ -139,7 +132,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put("settings_search_always_expand", "true");
- DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true");
DEFAULT_FLAGS.put(SETTINGS_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE, "false");
DEFAULT_FLAGS.put(SETTINGS_APP_LOCALE_OPT_IN_ENABLED, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
@@ -154,7 +146,6 @@ public class FeatureFlagUtils {
private static final Set<String> PERSISTENT_FLAGS;
static {
PERSISTENT_FLAGS = new HashSet<>();
- PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
PERSISTENT_FLAGS.add(SETTINGS_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE);
PERSISTENT_FLAGS.add(SETTINGS_APP_LOCALE_OPT_IN_ENABLED);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index a6f63e859049..510bde1deb5d 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -89,9 +89,7 @@ public final class AccessibilityInteractionController {
// Callbacks should have the same configuration of the flags below to allow satisfying a pending
// node request on prefetch
- private static final int FLAGS_AFFECTING_REPORTED_DATA =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_REPORT_MASK;
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -167,6 +165,11 @@ public final class AccessibilityInteractionController {
return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
}
+ private boolean isVisibleToAccessibilityService(View view) {
+ return view != null && (mA11yManager.isRequestFromAccessibilityTool()
+ || !view.isAccessibilityDataPrivate());
+ }
+
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
@@ -358,7 +361,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
requestedView = findViewByAccessibilityId(accessibilityViewId);
if (requestedView != null && isShown(requestedView)) {
requestedNode = populateAccessibilityNodeInfoForView(
@@ -371,7 +374,7 @@ public final class AccessibilityInteractionController {
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
} finally {
@@ -396,7 +399,7 @@ public final class AccessibilityInteractionController {
}
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
@@ -478,7 +481,7 @@ public final class AccessibilityInteractionController {
|| viewId == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null) {
final int resolvedViewId = root.getContext().getResources()
@@ -494,7 +497,7 @@ public final class AccessibilityInteractionController {
mAddNodeInfosForViewId.reset();
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -542,7 +545,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
@@ -561,7 +564,7 @@ public final class AccessibilityInteractionController {
final int viewCount = foundViews.size();
for (int i = 0; i < viewCount; i++) {
View foundView = foundViews.get(i);
- if (isShown(foundView)) {
+ if (isShown(foundView) && isVisibleToAccessibilityService(foundView)) {
provider = foundView.getAccessibilityNodeProvider();
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
@@ -579,7 +582,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -627,7 +630,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
switch (focusType) {
@@ -642,6 +645,9 @@ public final class AccessibilityInteractionController {
if (!isShown(host)) {
break;
}
+ if (!isVisibleToAccessibilityService(host)) {
+ break;
+ }
// If the host has a provider ask this provider to search for the
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
@@ -662,6 +668,9 @@ public final class AccessibilityInteractionController {
if (!isShown(target)) {
break;
}
+ if (!isVisibleToAccessibilityService(target)) {
+ break;
+ }
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
focused = provider.findFocus(focusType);
@@ -675,7 +684,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
focused, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -722,7 +731,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
View nextView = root.focusSearch(direction);
@@ -731,7 +740,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
next, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -778,9 +787,9 @@ public final class AccessibilityInteractionController {
mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View target = findViewByAccessibilityId(accessibilityViewId);
- if (target != null && isShown(target)) {
+ if (target != null && isShown(target) && isVisibleToAccessibilityService(target)) {
mA11yManager.notifyPerformingAction(action);
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
@@ -799,7 +808,7 @@ public final class AccessibilityInteractionController {
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
callback.setPerformAccessibilityActionResult(succeeded, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -823,9 +832,9 @@ public final class AccessibilityInteractionController {
return;
}
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- final View root = mViewRootImpl.mView;
+ setAccessibilityFetchFlags(
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS);
+ final View root = getRootView();
if (root != null && isShown(root)) {
final View host = mViewRootImpl.mAccessibilityFocusedHost;
// If there is no accessibility focus host or it is not a descendant
@@ -849,7 +858,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
@@ -869,7 +878,7 @@ public final class AccessibilityInteractionController {
|| mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- final View root = mViewRootImpl.mView;
+ final View root = getRootView();
if (root != null && isShown(root)) {
// trigger ACTION_OUTSIDE to notify windows
final long now = SystemClock.uptimeMillis();
@@ -882,12 +891,30 @@ public final class AccessibilityInteractionController {
private View findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
- return mViewRootImpl.mView;
+ return getRootView();
} else {
return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
}
}
+ private View getRootView() {
+ if (!isVisibleToAccessibilityService(mViewRootImpl.mView)) {
+ return null;
+ }
+ return mViewRootImpl.mView;
+ }
+
+ private void setAccessibilityFetchFlags(int flags) {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ mA11yManager.setRequestFromAccessibilityTool(
+ (flags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) != 0);
+ }
+
+ private void resetAccessibilityFetchFlags() {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ mA11yManager.setRequestFromAccessibilityTool(false);
+ }
+
// The boundInScreen includes magnification effect, so we need to normalize it before
// determine the visibility.
private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
@@ -1706,7 +1733,7 @@ public final class AccessibilityInteractionController {
@Override
public boolean test(View view) {
- if (view.getId() == mViewId && isShown(view)) {
+ if (view.getId() == mViewId && isShown(view) && isVisibleToAccessibilityService(view)) {
mInfos.add(view.createAccessibilityNodeInfo());
}
return false;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8aa113dfc8e6..229de31ddcba 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -413,11 +413,6 @@ interface IWindowManager
boolean hasNavigationBar(int displayId);
/**
- * Get the position of the nav bar
- */
- int getNavBarPosition(int displayId);
-
- /**
* Lock the device immediately with the specified options (can be null).
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 9f426a176a08..45d28da73c6b 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -351,6 +351,20 @@ public class InsetsState implements Parcelable {
return insets;
}
+ // TODO: Remove this once the task bar is treated as navigation bar.
+ public Insets calculateInsetsWithInternalTypes(Rect frame, @InternalInsetsType int[] types,
+ boolean ignoreVisibility) {
+ Insets insets = Insets.NONE;
+ for (int i = types.length - 1; i >= 0; i--) {
+ InsetsSource source = mSources[types[i]];
+ if (source == null) {
+ continue;
+ }
+ insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets);
+ }
+ return insets;
+ }
+
public Insets calculateInsets(Rect frame, @InsetsType int types,
InsetsVisibilities overrideVisibilities) {
Insets insets = Insets.NONE;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 48937770eddb..996dee0cd2da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3085,6 +3085,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
/**
+ * Automatically determine whether the view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Accessibility interactions from services without {@code isAccessibilityTool} set to true are
+ * disallowed for any of the following conditions:
+ * <li>this view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.</li>
+ * <li>this view sets {@link #getFilterTouchesWhenObscured()}.</li>
+ * <li>any parent of this view returns true from {@link #isAccessibilityDataPrivate()}.</li>
+ * </p>
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0x00000000;
+
+ /**
+ * Only allow interactions from {@link android.accessibilityservice.AccessibilityService}s
+ * with the {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 0x00000001;
+
+ /**
+ * Allow interactions from all {@link android.accessibilityservice.AccessibilityService}s,
+ * regardless of their
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 0x00000002;
+
+ /** @hide */
+ @IntDef(prefix = { "ACCESSIBILITY_DATA_PRIVATE_" }, value = {
+ ACCESSIBILITY_DATA_PRIVATE_AUTO,
+ ACCESSIBILITY_DATA_PRIVATE_YES,
+ ACCESSIBILITY_DATA_PRIVATE_NO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AccessibilityDataPrivate {}
+
+ /**
* Mask for obtaining the bits which specify how to determine
* whether a view is important for accessibility.
*/
@@ -4527,6 +4566,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private CharSequence mAccessibilityPaneTitle;
/**
+ * Describes whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ private int mExplicitAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_AUTO;
+ /** Used to calculate and cache {@link #isAccessibilityDataPrivate()}. */
+ private int mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_AUTO;
+
+ /**
* Specifies the id of a view for which this view serves as a label for
* accessibility purposes.
*/
@@ -5919,6 +5968,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
+ case R.styleable.View_accessibilityDataPrivate:
+ setAccessibilityDataPrivate(a.getInt(attr,
+ ACCESSIBILITY_DATA_PRIVATE_AUTO));
+ break;
case R.styleable.View_accessibilityLiveRegion:
setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
break;
@@ -8518,6 +8571,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* is responsible for handling this call.
* </p>
* <p>
+ * If this view sets {@link #isAccessibilityDataPrivate()} then this view should only append
+ * sensitive information to an event that also sets
+ * {@link AccessibilityEvent#isAccessibilityDataPrivate()}.
+ * </p>
+ * <p>
* <em>Note:</em> Accessibility events of certain types are not dispatched for
* populating the event text via this method. For details refer to {@link AccessibilityEvent}.
* </p>
@@ -10419,7 +10477,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if ((mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS) != 0
&& Resources.resourceHasPackage(mID)) {
try {
String viewId = getResources().getResourceName(mID);
@@ -13200,6 +13258,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void setFilterTouchesWhenObscured(boolean enabled) {
setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0,
FILTER_TOUCHES_WHEN_OBSCURED);
+ calculateAccessibilityDataPrivate();
}
/**
@@ -14402,14 +14461,80 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@UnsupportedAppUsage
public boolean includeForAccessibility() {
if (mAttachInfo != null) {
+ if (!AccessibilityManager.getInstance(mContext).isRequestFromAccessibilityTool()
+ && isAccessibilityDataPrivate()) {
+ return false;
+ }
+
return (mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
|| isImportantForAccessibility();
}
return false;
}
/**
+ * Whether this view should restrict accessibility service access only to services that have the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * See default behavior provided by {@link #ACCESSIBILITY_DATA_PRIVATE_AUTO}. Otherwise,
+ * returns true for {@link #ACCESSIBILITY_DATA_PRIVATE_YES} or false for {@link
+ * #ACCESSIBILITY_DATA_PRIVATE_NO}.
+ * </p>
+ *
+ * @return True if this view should restrict accessibility service access to services that have
+ * the isAccessibilityTool property.
+ */
+ @ViewDebug.ExportedProperty(category = "accessibility")
+ public boolean isAccessibilityDataPrivate() {
+ if (mInferredAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_AUTO) {
+ calculateAccessibilityDataPrivate();
+ }
+ return mInferredAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_YES;
+ }
+
+ /**
+ * Calculate and cache the inferred value for {@link #isAccessibilityDataPrivate()}.
+ *
+ * <p>
+ * <strong>Note:</strong> This method needs to be called any time one of the below conditions
+ * changes, to recalculate the new value.
+ * </p>
+ */
+ void calculateAccessibilityDataPrivate() {
+ // Use the explicit value if set.
+ if (mExplicitAccessibilityDataPrivate != ACCESSIBILITY_DATA_PRIVATE_AUTO) {
+ mInferredAccessibilityDataPrivate = mExplicitAccessibilityDataPrivate;
+ } else if (mAttachInfo != null && mAttachInfo.mWindowSecure) {
+ // Views inside FLAG_SECURE windows default to accessibilityDataPrivate.
+ mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_YES;
+ } else if (getFilterTouchesWhenObscured()) {
+ // Views that set filterTouchesWhenObscured default to accessibilityDataPrivate.
+ mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_YES;
+ } else if (mParent instanceof View && ((View) mParent).isAccessibilityDataPrivate()) {
+ // Descendants of an accessibilityDataPrivate View are also accessibilityDataPrivate.
+ mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_YES;
+ } else {
+ // Otherwise, default to not accessibilityDataPrivate.
+ mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_NO;
+ }
+ }
+
+ /**
+ * Specifies whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ public void setAccessibilityDataPrivate(
+ @AccessibilityDataPrivate int accessibilityDataPrivate) {
+ mExplicitAccessibilityDataPrivate = accessibilityDataPrivate;
+ calculateAccessibilityDataPrivate();
+ }
+
+ /**
* Returns whether the View is considered actionable from
* accessibility perspective. Such view are important for
* accessibility.
@@ -30096,6 +30221,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int mWindowVisibility;
/**
+ * Indicates whether the view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.
+ */
+ boolean mWindowSecure;
+
+ /**
* Indicates the time at which drawing started to occur.
*/
@UnsupportedAppUsage
@@ -30272,8 +30402,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Flags related to accessibility processing.
*
- * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
*/
int mAccessibilityFetchFlags;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9f0ad1169a8e..c4f20dc25904 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3919,6 +3919,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
+ void calculateAccessibilityDataPrivate() {
+ super.calculateAccessibilityDataPrivate();
+ for (int i = 0; i < mChildrenCount; i++) {
+ mChildren[i].calculateAccessibilityDataPrivate();
+ }
+ }
+
+ @Override
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void dispatchDetachedFromWindow() {
// If we still have a touch target, we are still in the process of
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 59bc061f54aa..ba250b0e247e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2836,6 +2836,7 @@ public final class ViewRootImpl implements ViewParent,
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mWindowVisibility = viewVisibility;
+ mAttachInfo.mWindowSecure = (lp.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 57a0330e3c18..5ed9d2f90a72 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -118,11 +118,11 @@ public class WindowLayout {
}
if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
if (displayFrame.width() < displayFrame.height()) {
- displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
- displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+ displayCutoutSafeExceptMaybeBars.top = MIN_Y;
+ displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
} else {
- displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
- displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+ displayCutoutSafeExceptMaybeBars.left = MIN_X;
+ displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
}
final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0;
@@ -132,23 +132,23 @@ public class WindowLayout {
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, WindowInsets.Type.systemBars(), requestedVisibilities);
if (systemBarsInsets.left > 0) {
- displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+ displayCutoutSafeExceptMaybeBars.left = MIN_X;
}
if (systemBarsInsets.top > 0) {
- displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+ displayCutoutSafeExceptMaybeBars.top = MIN_Y;
}
if (systemBarsInsets.right > 0) {
- displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+ displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
if (systemBarsInsets.bottom > 0) {
- displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+ displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
if (type == TYPE_INPUT_METHOD) {
final InsetsSource navSource = state.peekSource(ITYPE_NAVIGATION_BAR);
if (navSource != null && navSource.calculateInsets(displayFrame, true).bottom > 0) {
// The IME can always extend under the bottom cutout if the navbar is there.
- displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+ displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index cd0dd1df1249..2db0dcbce45e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -46,15 +46,17 @@ import java.util.List;
* </p>
* <p>
* The main purpose of an accessibility event is to communicate changes in the UI to an
- * {@link android.accessibilityservice.AccessibilityService}. The service may then inspect,
- * if needed the user interface by examining the View hierarchy, as represented by a tree of
- * {@link AccessibilityNodeInfo}s (snapshot of a View state)
- * which can be used for exploring the window content. Note that the privilege for accessing
- * an event's source, thus the window content, has to be explicitly requested. For more
+ * {@link android.accessibilityservice.AccessibilityService}. If needed, the service may then
+ * inspect the user interface by examining the View hierarchy through the event's
+ * {@link #getSource() source}, as represented by a tree of {@link AccessibilityNodeInfo}s (snapshot
+ * of a View state) which can be used for exploring the window content. Note that the privilege for
+ * accessing an event's source, thus the window content, has to be explicitly requested. For more
* details refer to {@link android.accessibilityservice.AccessibilityService}. If an
* accessibility service has not requested to retrieve the window content the event will
- * not contain reference to its source. Also for events of type
- * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available.
+ * not contain reference to its source. <strong>Note: </strong> for events of type
+ * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available, and Views that set
+ * {@link android.view.View#isAccessibilityDataPrivate()} may not populate all event properties on
+ * events sent from higher up in the view hierarchy.
* </p>
* <p>
* This class represents various semantically different accessibility event
@@ -1092,6 +1094,47 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Initial value matches the {@link android.view.View#isAccessibilityDataPrivate} property from
+ * the event's source node, if present, or false by default.
+ * </p>
+ *
+ * @return True if the event should be delivered only to isAccessibilityTool services, false
+ * otherwise.
+ * @see #setAccessibilityDataPrivate
+ */
+ @Override
+ public boolean isAccessibilityDataPrivate() {
+ return super.isAccessibilityDataPrivate();
+ }
+
+ /**
+ * Sets whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * This will be set automatically based on the event's source (if present). If creating and
+ * sending an event directly through {@link AccessibilityManager} (where an event may have
+ * no source) then this method must be called explicitly if you want non-default behavior.
+ * </p>
+ *
+ * @param accessibilityDataPrivate True if the event should be delivered only to
+ * isAccessibilityTool services, false otherwise.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ @Override
+ public void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ super.setAccessibilityDataPrivate(accessibilityDataPrivate);
+ }
+
+ /**
* Gets the bit mask of the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
*
* @see #SPEECH_STATE_SPEAKING_START
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 9e3195aec8a6..e89f836aaac1 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -276,6 +276,8 @@ public final class AccessibilityManager {
private final ArrayMap<AudioDescriptionRequestedChangeListener, Executor>
mAudioDescriptionRequestedChangeListeners = new ArrayMap<>();
+ private boolean mRequestFromAccessibilityTool;
+
/**
* Map from a view's accessibility id to the list of request preparers set for that view
*/
@@ -983,6 +985,39 @@ public final class AccessibilityManager {
}
/**
+ * Whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * <p>
+ * You can use this method inside {@link AccessibilityNodeProvider} to decide how to populate
+ * your nodes.
+ * </p>
+ *
+ * <p>
+ * <strong>Note:</strong> The return value is valid only when an {@link AccessibilityNodeInfo}
+ * request is in progress, can change from one request to another, and has no meaning when a
+ * request is not in progress.
+ * </p>
+ *
+ * @return True if the current request is from a tool that sets isAccessibilityTool.
+ */
+ public boolean isRequestFromAccessibilityTool() {
+ return mRequestFromAccessibilityTool;
+ }
+
+ /**
+ * Specifies whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * @hide
+ */
+ public void setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool) {
+ mRequestFromAccessibilityTool = requestFromAccessibilityTool;
+ }
+
+ /**
* Registers a {@link AccessibilityRequestPreparer}.
*/
public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5d527500ff7b..6ae59bf0c8fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -220,14 +220,29 @@ public class AccessibilityNodeInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface PrefetchingStrategy {}
- /** @hide */
- public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
- /** @hide */
- public static final int FLAG_REPORT_VIEW_IDS = 0x00000100;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS = 0x00000100;
+
+ /**
+ * @see AccessibilityServiceInfo#isAccessibilityTool()
+ * @hide
+ */
+ public static final int FLAG_SERVICE_IS_ACCESSIBILITY_TOOL = 0x00000200;
/** @hide */
- public static final int FLAG_REPORT_MASK = 0x00000180;
+ public static final int FLAG_REPORT_MASK =
+ FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ | FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
+ | FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
// Actions.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 036316e15cb9..789c740bbba2 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -72,6 +72,7 @@ public class AccessibilityRecord {
private static final int PROPERTY_FULL_SCREEN = 0x00000080;
private static final int PROPERTY_SCROLLABLE = 0x00000100;
private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
+ private static final int PROPERTY_ACCESSIBILITY_DATA_PRIVATE = 0x00000400;
private static final int GET_SOURCE_PREFETCH_FLAGS =
AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS
@@ -159,6 +160,8 @@ public class AccessibilityRecord {
important = root.isImportantForAccessibility();
rootViewId = root.getAccessibilityViewId();
mSourceWindowId = root.getAccessibilityWindowId();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE,
+ root.isAccessibilityDataPrivate());
}
setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
@@ -388,6 +391,23 @@ public class AccessibilityRecord {
}
/**
+ * @see AccessibilityEvent#isAccessibilityDataPrivate
+ * @hide
+ */
+ boolean isAccessibilityDataPrivate() {
+ return getBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE);
+ }
+
+ /**
+ * @see AccessibilityEvent#setAccessibilityDataPrivate
+ * @hide
+ */
+ void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ enforceNotSealed();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE, accessibilityDataPrivate);
+ }
+
+ /**
* Gets the number of items that can be visited.
*
* @return The number of items.
@@ -941,6 +961,8 @@ public class AccessibilityRecord {
appendUnless(false, PROPERTY_CHECKED, builder);
appendUnless(false, PROPERTY_FULL_SCREEN, builder);
appendUnless(false, PROPERTY_SCROLLABLE, builder);
+ appendUnless(false, PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, builder);
+ appendUnless(false, PROPERTY_ACCESSIBILITY_DATA_PRIVATE, builder);
append(builder, "BeforeText", mBeforeText);
append(builder, "FromIndex", mFromIndex);
@@ -974,6 +996,8 @@ public class AccessibilityRecord {
case PROPERTY_SCROLLABLE: return "Scrollable";
case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY:
return "ImportantForAccessibility";
+ case PROPERTY_ACCESSIBILITY_DATA_PRIVATE:
+ return "AccessibilityDataPrivate";
default: return Integer.toHexString(prop);
}
}
diff --git a/core/java/android/view/accessibility/DirectAccessibilityConnection.java b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
index 71746ee5dbab..dcc5660a64d6 100644
--- a/core/java/android/view/accessibility/DirectAccessibilityConnection.java
+++ b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
@@ -55,8 +55,8 @@ class DirectAccessibilityConnection extends IAccessibilityServiceConnection.Defa
// Fetch all views, but do not use prefetching/cache since this "connection" does not
// receive cache invalidation events (as it is not linked to an AccessibilityService).
private static final int FETCH_FLAGS =
- AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS
- | AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
+ | AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
private static final MagnificationSpec MAGNIFICATION_SPEC = new MagnificationSpec();
private static final int PID = Process.myPid();
private static final Region INTERACTIVE_REGION = null;
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index bfe6ae6447d0..978bfc7af60d 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -410,4 +410,11 @@ public interface InputMethod {
// intentionally empty
}
+ /**
+ * Remove stylus handwriting window.
+ * @hide
+ */
+ default void removeStylusHandwritingWindow() {
+ // intentionally empty
+ }
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index e31eabb083e5..0b63c179d042 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -303,6 +303,10 @@ public final class TextServicesManager {
/**
* Retrieve the list of currently enabled spell checkers.
*
+ * <p> Note: The results are filtered by the rules of
+ * <a href="/training/basics/intents/package-visibility">package visibility</a>, except for
+ * the currently active spell checker.
+ *
* @return The list of currently enabled spell checkers.
*/
@UserHandleAware
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index fa84407c5c4d..7314ad83bd0c 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -732,6 +732,10 @@ public class LinearLayout extends ViewGroup {
* @hide Pending API consideration. Currently only used internally by the system.
*/
protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (mShowDividers == SHOW_DIVIDER_NONE) {
+ // Short-circuit to save iteration over child views.
+ return false;
+ }
if (childIndex == getVirtualChildCount()) {
// Check whether the end divider should draw.
return (mShowDividers & SHOW_DIVIDER_END) != 0;
@@ -746,6 +750,24 @@ public class LinearLayout extends ViewGroup {
}
/**
+ * Determines whether or not there's a divider after a specified child index.
+ *
+ * @param childIndex Index of child to check for following divider
+ * @return true if there should be a divider after the child at childIndex
+ */
+ private boolean hasDividerAfterChildAt(int childIndex) {
+ if (mShowDividers == SHOW_DIVIDER_NONE) {
+ // Short-circuit to save iteration over child views.
+ return false;
+ }
+ if (allViewsAreGoneAfter(childIndex)) {
+ // This is the last view that's not gone, check if end divider is enabled.
+ return (mShowDividers & SHOW_DIVIDER_END) != 0;
+ }
+ return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
+ }
+
+ /**
* Checks whether all (virtual) child views before the given index are gone.
*/
private boolean allViewsAreGoneBefore(int childIndex) {
@@ -759,6 +781,20 @@ public class LinearLayout extends ViewGroup {
}
/**
+ * Checks whether all (virtual) child views after the given index are gone.
+ */
+ private boolean allViewsAreGoneAfter(int childIndex) {
+ final int count = getVirtualChildCount();
+ for (int i = childIndex + 1; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+ if (child != null && child.getVisibility() != GONE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Measures the children when the orientation of this LinearLayout is set
* to {@link #VERTICAL}.
*
@@ -1295,6 +1331,7 @@ public class LinearLayout extends ViewGroup {
if (useLargestChild &&
(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
+ nonSkippedChildCount = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1308,6 +1345,11 @@ public class LinearLayout extends ViewGroup {
continue;
}
+ nonSkippedChildCount++;
+ if (hasDividerBeforeChildAt(i)) {
+ mTotalLength += mDividerWidth;
+ }
+
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
if (isExactly) {
@@ -1319,6 +1361,10 @@ public class LinearLayout extends ViewGroup {
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
+
+ if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
+ mTotalLength += mDividerWidth;
+ }
}
// Add in our padding
@@ -1347,6 +1393,7 @@ public class LinearLayout extends ViewGroup {
maxHeight = -1;
mTotalLength = 0;
+ nonSkippedChildCount = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1354,6 +1401,11 @@ public class LinearLayout extends ViewGroup {
continue;
}
+ nonSkippedChildCount++;
+ if (hasDividerBeforeChildAt(i)) {
+ mTotalLength += mDividerWidth;
+ }
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final float childWeight = lp.weight;
if (childWeight > 0) {
@@ -1423,6 +1475,10 @@ public class LinearLayout extends ViewGroup {
}
}
+ if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
+ mTotalLength += mDividerWidth;
+ }
+
// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;
// TODO: Should we update widthSize with the new total length?
@@ -1810,7 +1866,13 @@ public class LinearLayout extends ViewGroup {
break;
}
- if (hasDividerBeforeChildAt(childIndex)) {
+ if (isLayoutRtl) {
+ // Because rtl rendering occurs in the reverse direction, we need to check
+ // after the child rather than before (since after=left in this context)
+ if (hasDividerAfterChildAt(childIndex)) {
+ childLeft += mDividerWidth;
+ }
+ } else if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index f946fe6d8314..ec0d86295f8a 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -16,17 +16,22 @@
package android.widget;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
+import android.util.PluralsMessageFormatter;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import java.util.HashMap;
+
+
/**
* A RatingBar is an extension of SeekBar and ProgressBar that shows a rating in
* stars. The user can touch/drag or use arrow keys to set the rating when using
@@ -53,6 +58,20 @@ import com.android.internal.R;
public class RatingBar extends AbsSeekBar {
/**
+ * Key used for generating Text-to-Speech output regarding the current star rating.
+ * @hide
+ */
+ @TestApi
+ public static final String PLURALS_RATING = "rating";
+
+ /**
+ * Key used for generating Text-to-Speech output regarding the maximum star count.
+ * @hide
+ */
+ @TestApi
+ public static final String PLURALS_MAX = "max";
+
+ /**
* A callback that notifies clients when the rating has been changed. This
* includes changes that were initiated by the user through a touch gesture
* or arrow key/trackball as well as changes that were initiated
@@ -354,6 +373,16 @@ public class RatingBar extends AbsSeekBar {
if (canUserSetProgress()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS);
}
+
+ final float scaledMax = getMax() * getStepSize();
+ final HashMap<String, Object> params = new HashMap();
+ params.put(PLURALS_RATING, getRating());
+ params.put(PLURALS_MAX, scaledMax);
+ info.setStateDescription(PluralsMessageFormatter.format(
+ getContext().getResources(),
+ params,
+ R.string.rating_label
+ ));
}
@Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1c7c5829d2bc..6a572c530a1e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4891,24 +4891,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* <p>Line-break style specifies the line-break strategies that can be used
* for text wrapping. The line-break style affects rule-based line breaking
- * by specifying the strictness of line-breaking rules.</p>
+ * by specifying the strictness of line-breaking rules.
*
- * <p>The following are types of line-break styles:</p>
+ * <p>The following are types of line-break styles:
* <ul>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE}</li>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL}</li>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}</li>
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE}
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL}
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}
* </ul>
*
* <p>The default line-break style is
* {@link LineBreakConfig#LINE_BREAK_STYLE_NONE}, which specifies that no
- * line-breaking rules are used.</p>
+ * line-breaking rules are used.
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
- * line-break property</a> for more information.</p>
+ * line-break property</a> for more information.
*
- * @param lineBreakStyle The line break style for the text.
+ * @param lineBreakStyle The line-break style for the text.
*/
public void setLineBreakStyle(@LineBreakConfig.LineBreakStyle int lineBreakStyle) {
if (mLineBreakStyle != lineBreakStyle) {
@@ -4927,17 +4927,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* <p>The line-break word style affects dictionary-based line breaking by
* providing phrase-based line-breaking opportunities. Use
* {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE} to specify
- * phrase-based line breaking.</p>
+ * phrase-based line breaking.
*
* <p>The default line-break word style is
* {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}, which specifies that
- * no line-breaking word style is used.</p>
+ * no line-breaking word style is used.
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#word-break-property" class="external">
- * word-break property</a> for more information.</p>
+ * word-break property</a> for more information.
*
- * @param lineBreakWordStyle The line break word style for the text.
+ * @param lineBreakWordStyle The line-break word style for the text.
*/
public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
mUserSpeficiedLineBreakwordStyle = true;
@@ -12089,6 +12089,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
super.onPopulateAccessibilityEventInternal(event);
+ if (this.isAccessibilityDataPrivate() && !event.isAccessibilityDataPrivate()) {
+ // This view's accessibility data is private, but another view that generated this event
+ // is not, so don't append this view's text to the event in order to prevent sharing
+ // this view's contents with non-accessibility-tool services.
+ return;
+ }
+
final CharSequence text = getTextForAccessibility();
if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index b263b08d6abc..dc1f612534e2 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -119,6 +119,12 @@ public final class TransitionInfo implements Parcelable {
/** The container is going to show IME on its task after the transition. */
public static final int FLAG_WILL_IME_SHOWN = 1 << 11;
+ /** The container attaches owner profile thumbnail for cross profile animation. */
+ public static final int FLAG_CROSS_PROFILE_OWNER_THUMBNAIL = 1 << 12;
+
+ /** The container attaches work profile thumbnail for cross profile animation. */
+ public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13;
+
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
@@ -508,6 +514,11 @@ public final class TransitionInfo implements Parcelable {
return mFlags;
}
+ /** Whether the given change flags has included in this change. */
+ public boolean hasFlags(@ChangeFlags int flags) {
+ return (mFlags & flags) != 0;
+ }
+
/**
* @return the bounds of the container before the change. It may be empty if the container
* is coming into existence.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f67e78572402..c8bc204bd9aa 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1283,9 +1283,6 @@ public class ResolverActivity extends Activity implements
}
if (target != null) {
- if (intent != null && isLaunchingTargetInOtherProfile()) {
- prepareIntentForCrossProfileLaunch(intent);
- }
safelyStartActivity(target);
// Rely on the ActivityManager to pop up a dialog regarding app suspension
@@ -1298,15 +1295,6 @@ public class ResolverActivity extends Activity implements
return true;
}
- private void prepareIntentForCrossProfileLaunch(Intent intent) {
- intent.fixUris(UserHandle.myUserId());
- }
-
- private boolean isLaunchingTargetInOtherProfile() {
- return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
- != UserHandle.myUserId();
- }
-
@VisibleForTesting
public void safelyStartActivity(TargetInfo cti) {
// We're dispatching intents that might be coming from legacy apps, so
@@ -1513,9 +1501,6 @@ public class ResolverActivity extends Activity implements
findViewById(R.id.button_open).setOnClickListener(v -> {
Intent intent = otherProfileResolveInfo.getResolvedIntent();
- if (intent != null) {
- prepareIntentForCrossProfileLaunch(intent);
- }
safelyStartActivityAsUser(otherProfileResolveInfo,
inactiveAdapter.mResolverListController.getUserHandle());
finish();
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 96cc5e1bd7d2..5f4a9cd5141e 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -172,12 +172,14 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+ prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
return true;
}
@Override
public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
+ prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
activity.startActivityAsUser(mResolvedIntent, options, user);
return false;
}
@@ -222,6 +224,13 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
}
};
+ private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
+ final int currentUserId = UserHandle.myUserId();
+ if (targetUserId != currentUserId) {
+ intent.fixUris(currentUserId);
+ }
+ }
+
private DisplayResolveInfo(Parcel in) {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 5db2e84845f5..9182d1dc56bf 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -40,7 +40,6 @@ oneway interface IInputMethod {
IBinder token;
IInputMethodPrivilegedOperations privilegedOperations;
int configChanges;
- boolean stylusHandWritingSupported;
int navigationBarFlags;
}
@@ -86,4 +85,6 @@ oneway interface IInputMethod {
void initInkWindow();
void finishStylusHandwriting();
+
+ void removeStylusHandwritingWindow();
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 962870e733f7..6909965edcd8 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -17,25 +17,35 @@
package com.android.internal.os;
import android.annotation.Nullable;
-import android.os.BatteryStats;
+import android.os.BatteryManager;
+import android.os.BatteryStats.HistoryItem;
+import android.os.BatteryStats.HistoryStepDetails;
+import android.os.BatteryStats.HistoryTag;
import android.os.Parcel;
+import android.os.ParcelFormatException;
+import android.os.Process;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ParseUtils;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
-import java.util.function.Supplier;
+import java.util.concurrent.locks.ReentrantLock;
/**
* BatteryStatsHistory encapsulates battery history files.
@@ -56,57 +66,62 @@ import java.util.function.Supplier;
* All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
* locks on BatteryStatsImpl object.
*/
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class BatteryStatsHistory {
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistory";
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- public static final int VERSION = 208;
+ private static final int VERSION = 208;
- public static final String HISTORY_DIR = "battery-history";
- public static final String FILE_SUFFIX = ".bin";
+ private static final String HISTORY_DIR = "battery-history";
+ private static final String FILE_SUFFIX = ".bin";
private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
+
// Part of initial delta int that specifies the time delta.
- public static final int DELTA_TIME_MASK = 0x7ffff;
- public static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
- public static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
- public static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
+ static final int DELTA_TIME_MASK = 0x7ffff;
+ static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
+ static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
+ static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
// Flag in delta int: a new battery level int follows.
- public static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
+ static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
// Flag in delta int: a new full state and battery status int follows.
- public static final int DELTA_STATE_FLAG = 0x00100000;
+ static final int DELTA_STATE_FLAG = 0x00100000;
// Flag in delta int: a new full state2 int follows.
- public static final int DELTA_STATE2_FLAG = 0x00200000;
+ static final int DELTA_STATE2_FLAG = 0x00200000;
// Flag in delta int: contains a wakelock or wakeReason tag.
- public static final int DELTA_WAKELOCK_FLAG = 0x00400000;
+ static final int DELTA_WAKELOCK_FLAG = 0x00400000;
// Flag in delta int: contains an event description.
- public static final int DELTA_EVENT_FLAG = 0x00800000;
+ static final int DELTA_EVENT_FLAG = 0x00800000;
// Flag in delta int: contains the battery charge count in uAh.
- public static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000;
+ static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000;
// These upper bits are the frequently changing state bits.
- public static final int DELTA_STATE_MASK = 0xfe000000;
+ static final int DELTA_STATE_MASK = 0xfe000000;
// These are the pieces of battery state that are packed in to the upper bits of
// the state int that have been packed in to the first delta int. They must fit
// in STATE_BATTERY_MASK.
- public static final int STATE_BATTERY_MASK = 0xff000000;
- public static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
- public static final int STATE_BATTERY_STATUS_SHIFT = 29;
- public static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
- public static final int STATE_BATTERY_HEALTH_SHIFT = 26;
- public static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
- public static final int STATE_BATTERY_PLUG_SHIFT = 24;
+ static final int STATE_BATTERY_MASK = 0xff000000;
+ static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
+ static final int STATE_BATTERY_STATUS_SHIFT = 29;
+ static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
+ static final int STATE_BATTERY_HEALTH_SHIFT = 26;
+ static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
+ static final int STATE_BATTERY_PLUG_SHIFT = 24;
// We use the low bit of the battery state int to indicate that we have full details
// from a battery level change.
- public static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
+ static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
// Flag in history tag index: indicates that this is the first occurrence of this tag,
// therefore the tag value is written in the parcel
- public static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
+ static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
- @Nullable
- private final Supplier<Integer> mMaxHistoryFiles;
private final Parcel mHistoryBuffer;
+ private final File mSystemDir;
+ private final HistoryStepDetailsCalculator mStepDetailsCalculator;
private final File mHistoryDir;
+ private final Clock mClock;
+
+ private int mMaxHistoryFiles;
+ private int mMaxHistoryBufferSize;
+
/**
* The active history file that the history buffer is backed up into.
*/
@@ -144,19 +159,77 @@ public class BatteryStatsHistory {
*/
private int mParcelIndex = 0;
+ private final ReentrantLock mWriteLock = new ReentrantLock();
+
+ private final HistoryItem mHistoryCur = new HistoryItem();
+
+ private boolean mHaveBatteryLevel;
+ private boolean mRecordingHistory;
+
+ private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
+ private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
+
+ private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
+ private SparseArray<HistoryTag> mHistoryTags;
+ private final HistoryItem mHistoryLastWritten = new HistoryItem();
+ private final HistoryItem mHistoryLastLastWritten = new HistoryItem();
+ private final HistoryItem mHistoryAddTmp = new HistoryItem();
+ private int mNextHistoryTagIdx = 0;
+ private int mNumHistoryTagChars = 0;
+ private int mHistoryBufferLastPos = -1;
+ private int mActiveHistoryStates = 0xffffffff;
+ private int mActiveHistoryStates2 = 0xffffffff;
+ private long mLastHistoryElapsedRealtimeMs = 0;
+ private long mTrackRunningHistoryElapsedRealtimeMs = 0;
+ private long mTrackRunningHistoryUptimeMs = 0;
+ private long mHistoryBaseTimeMs;
+
+ private byte mLastHistoryStepLevel = 0;
+
+ private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
+
+ /**
+ * A delegate responsible for computing additional details for a step in battery history.
+ */
+ public interface HistoryStepDetailsCalculator {
+ /**
+ * Returns additional details for the current history step or null.
+ */
+ @Nullable
+ HistoryStepDetails getHistoryStepDetails();
+
+ /**
+ * Resets the calculator to get ready for a new battery session
+ */
+ void clear();
+ }
+
/**
* Constructor
*
- * @param historyBuffer The in-memory history buffer.
- * @param systemDir typically /data/system
- * @param maxHistoryFiles the largest number of history buffer files to keep
+ * @param systemDir typically /data/system
+ * @param maxHistoryFiles the largest number of history buffer files to keep
+ * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
*/
+ public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize,
+ stepDetailsCalculator, clock);
+ initHistoryBuffer();
+ }
+
+ @VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
- Supplier<Integer> maxHistoryFiles) {
+ int maxHistoryFiles, int maxHistoryBufferSize,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
mHistoryBuffer = historyBuffer;
- mHistoryDir = new File(systemDir, HISTORY_DIR);
+ mSystemDir = systemDir;
mMaxHistoryFiles = maxHistoryFiles;
+ mMaxHistoryBufferSize = maxHistoryBufferSize;
+ mStepDetailsCalculator = stepDetailsCalculator;
+ mClock = clock;
+ mHistoryDir = new File(systemDir, HISTORY_DIR);
mHistoryDir.mkdirs();
if (!mHistoryDir.exists()) {
Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
@@ -192,19 +265,81 @@ public class BatteryStatsHistory {
}
}
+ public BatteryStatsHistory(HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ mStepDetailsCalculator = stepDetailsCalculator;
+ mClock = clock;
+
+ mHistoryBuffer = Parcel.obtain();
+ mSystemDir = null;
+ mHistoryDir = null;
+ initHistoryBuffer();
+ }
+
/**
* Used when BatteryStatsImpl object is created from deserialization of a parcel,
- * such as Settings app or checkin file.
- * @param historyBuffer the history buffer
+ * such as a checkin file.
*/
- public BatteryStatsHistory(Parcel historyBuffer) {
- mHistoryDir = null;
+ private BatteryStatsHistory(Parcel historyBuffer,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
mHistoryBuffer = historyBuffer;
- mMaxHistoryFiles = null;
+ mClock = clock;
+ mSystemDir = null;
+ mHistoryDir = null;
+ mStepDetailsCalculator = stepDetailsCalculator;
+ }
+
+ private void initHistoryBuffer() {
+ mHistoryBaseTimeMs = 0;
+ mLastHistoryElapsedRealtimeMs = 0;
+ mTrackRunningHistoryElapsedRealtimeMs = 0;
+ mTrackRunningHistoryUptimeMs = 0;
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
+ mHistoryLastLastWritten.clear();
+ mHistoryLastWritten.clear();
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+ mHistoryBufferLastPos = -1;
+ mActiveHistoryStates = 0xffffffff;
+ mActiveHistoryStates2 = 0xffffffff;
+ if (mStepDetailsCalculator != null) {
+ mStepDetailsCalculator.clear();
+ }
+ }
+
+ /**
+ * Changes the maximum number of history files to be kept.
+ */
+ public void setMaxHistoryFiles(int maxHistoryFiles) {
+ mMaxHistoryFiles = maxHistoryFiles;
+ }
+
+ /**
+ * Changes the maximum size of the history buffer, in bytes.
+ */
+ public void setMaxHistoryBufferSize(int maxHistoryBufferSize) {
+ mMaxHistoryBufferSize = maxHistoryBufferSize;
}
- public File getHistoryDirectory() {
- return mHistoryDir;
+ /**
+ * Creates a read-only copy of the battery history. Does not copy the files stored
+ * in the system directory, so it is not safe while actively writing history.
+ */
+ public BatteryStatsHistory copy() {
+ // Make a copy of battery history to avoid concurrent modification.
+ Parcel historyBuffer = Parcel.obtain();
+ historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+ return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null);
+ }
+
+ /**
+ * Returns true if this instance only supports reading history.
+ */
+ public boolean isReadOnly() {
+ return mActiveFile == null;
}
/**
@@ -221,12 +356,13 @@ public class BatteryStatsHistory {
/**
* Create history AtomicFile from file number.
+ *
* @param num file number.
* @return AtomicFile object.
*/
private AtomicFile getFile(int num) {
return new AtomicFile(
- new File(mHistoryDir, num + FILE_SUFFIX));
+ new File(mHistoryDir, num + FILE_SUFFIX));
}
/**
@@ -234,7 +370,7 @@ public class BatteryStatsHistory {
* create next history file.
*/
public void startNextFile() {
- if (mMaxHistoryFiles == null) {
+ if (mMaxHistoryFiles == 0) {
Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
return;
}
@@ -264,7 +400,7 @@ public class BatteryStatsHistory {
// if there are more history files than allowed, delete oldest history files.
// mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and can be updated by GService
// config at run time.
- while (mFileNumbers.size() > mMaxHistoryFiles.get()) {
+ while (mFileNumbers.size() > mMaxHistoryFiles) {
int oldest = mFileNumbers.get(0);
getFile(oldest).delete();
mFileNumbers.remove(0);
@@ -272,36 +408,43 @@ public class BatteryStatsHistory {
}
/**
- * Delete all existing history files. Active history file start from number 0 again.
+ * Clear history buffer and delete all existing history files. Active history file start from
+ * number 0 again.
*/
- public void resetAllFiles() {
+ public void reset() {
+ if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!");
for (Integer i : mFileNumbers) {
getFile(i).delete();
}
mFileNumbers.clear();
mFileNumbers.add(0);
setActiveFile(0);
+
+ initHistoryBuffer();
}
/**
* Start iterating history files and history buffer.
+ *
* @return always return true.
*/
- public boolean startIteratingHistory() {
+ public BatteryStatsHistoryIterator iterate() {
mRecordCount = 0;
mCurrentFileIndex = 0;
mCurrentParcel = null;
mCurrentParcelEnd = 0;
mParcelIndex = 0;
- return true;
+ mBatteryStatsHistoryIterator = new BatteryStatsHistoryIterator(this);
+ return mBatteryStatsHistoryIterator;
}
/**
* Finish iterating history files and history buffer.
*/
- public void finishIteratingHistory() {
+ void finishIteratingHistory() {
// setDataPosition so mHistoryBuffer Parcel can be written.
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ mBatteryStatsHistoryIterator = null;
if (DEBUG) {
Slog.d(TAG, "Battery history records iterated: " + mRecordCount);
}
@@ -311,11 +454,12 @@ public class BatteryStatsHistory {
* When iterating history files and history buffer, always start from the lowest numbered
* history file, when reached the mActiveFile (highest numbered history file), do not read from
* mActiveFile, read from history buffer instead because the buffer has more updated data.
+ *
* @param out a history item.
* @return The parcel that has next record. null if finished all history files and history
- * buffer
+ * buffer
*/
- public Parcel getNextParcel(BatteryStats.HistoryItem out) {
+ public Parcel getNextParcel(HistoryItem out) {
if (mRecordCount == 0) {
// reset out if it is the first record.
out.clear();
@@ -323,8 +467,7 @@ public class BatteryStatsHistory {
++mRecordCount;
// First iterate through all records in current parcel.
- if (mCurrentParcel != null)
- {
+ if (mCurrentParcel != null) {
if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
// There are more records in current parcel.
return mCurrentParcel;
@@ -389,7 +532,8 @@ public class BatteryStatsHistory {
/**
* Read history file into a parcel.
- * @param out the Parcel read into.
+ *
+ * @param out the Parcel read into.
* @param file the File to read from.
* @return true if success, false otherwise.
*/
@@ -402,8 +546,8 @@ public class BatteryStatsHistory {
Slog.d(TAG, "readFileToParcel:" + file.getBaseFile().getPath()
+ " duration ms:" + (SystemClock.uptimeMillis() - start));
}
- } catch(Exception e) {
- Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
return false;
}
out.unmarshall(raw, 0, raw.length);
@@ -413,6 +557,7 @@ public class BatteryStatsHistory {
/**
* Skip the header part of history parcel.
+ *
* @param p history parcel to skip head.
* @return true if version match, false if not.
*/
@@ -428,18 +573,68 @@ public class BatteryStatsHistory {
}
/**
+ * Writes the battery history contents for persistence.
+ */
+ public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
+ out.writeBoolean(inclHistory);
+ if (inclHistory) {
+ writeToParcel(out);
+ }
+
+ out.writeInt(mHistoryTagPool.size());
+ for (Map.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+ HistoryTag tag = ent.getKey();
+ out.writeInt(ent.getValue());
+ out.writeString(tag.string);
+ out.writeInt(tag.uid);
+ }
+ }
+
+ /**
+ * Reads battery history contents from a persisted parcel.
+ */
+ public void readSummaryFromParcel(Parcel in) {
+ boolean inclHistory = in.readBoolean();
+ if (inclHistory) {
+ readFromParcel(in);
+ }
+
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+
+ int numTags = in.readInt();
+ for (int i = 0; i < numTags; i++) {
+ int idx = in.readInt();
+ String str = in.readString();
+ int uid = in.readInt();
+ HistoryTag tag = new HistoryTag();
+ tag.string = str;
+ tag.uid = uid;
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(tag, idx);
+ if (idx >= mNextHistoryTagIdx) {
+ mNextHistoryTagIdx = idx + 1;
+ }
+ mNumHistoryTagChars += tag.string.length() + 1;
+ }
+ }
+
+ /**
* Read all history files and serialize into a big Parcel.
* Checkin file calls this method.
*
* @param out the output parcel
*/
public void writeToParcel(Parcel out) {
+ writeHistoryBuffer(out);
writeToParcel(out, false /* useBlobs */);
}
/**
* This is for Settings app, when Settings app receives big history parcel, it call
* this method to parse it into list of parcels.
+ *
* @param out the output parcel
*/
public void writeToBatteryUsageStatsParcel(Parcel out) {
@@ -450,13 +645,13 @@ public class BatteryStatsHistory {
private void writeToParcel(Parcel out, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
out.writeInt(mFileNumbers.size() - 1);
- for(int i = 0; i < mFileNumbers.size() - 1; i++) {
+ for (int i = 0; i < mFileNumbers.size() - 1; i++) {
AtomicFile file = getFile(mFileNumbers.get(i));
byte[] raw = new byte[0];
try {
raw = file.readFully();
- } catch(Exception e) {
- Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
}
if (useBlobs) {
out.writeBlob(raw);
@@ -480,17 +675,55 @@ public class BatteryStatsHistory {
Parcel historyBuffer = Parcel.obtain();
historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
- BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer);
+ BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer, null,
+ Clock.SYSTEM_CLOCK);
history.readFromParcel(in, true /* useBlobs */);
return history;
}
/**
+ * Read history from a check-in file.
+ */
+ public boolean readSummary() {
+ if (mActiveFile == null) {
+ Slog.w(TAG, "readSummary: no history file associated with this instance");
+ return false;
+ }
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ if (mActiveFile.exists()) {
+ byte[] raw = mActiveFile.readFully();
+ if (raw.length > 0) {
+ parcel.unmarshall(raw, 0, raw.length);
+ parcel.setDataPosition(0);
+ readHistoryBuffer(parcel);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "read history file::"
+ + mActiveFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " took ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading battery history", e);
+ reset();
+ return false;
+ } finally {
+ parcel.recycle();
+ }
+ return true;
+ }
+
+ /**
* This is for the check-in file, which has all history files embedded.
*
* @param in the input parcel.
*/
public void readFromParcel(Parcel in) {
+ readHistoryBuffer(in);
readFromParcel(in, false /* useBlobs */);
}
@@ -498,7 +731,7 @@ public class BatteryStatsHistory {
final long start = SystemClock.uptimeMillis();
mHistoryParcels = new ArrayList<>();
final int count = in.readInt();
- for(int i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) {
byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
if (temp == null || temp.length == 0) {
continue;
@@ -521,10 +754,12 @@ public class BatteryStatsHistory {
return stats.getAvailableBytes() > MIN_FREE_SPACE;
}
+ @VisibleForTesting
public List<Integer> getFilesNumbers() {
return mFileNumbers;
}
+ @VisibleForTesting
public AtomicFile getActiveFile() {
return mActiveFile;
}
@@ -534,15 +769,972 @@ public class BatteryStatsHistory {
*/
public int getHistoryUsedSize() {
int ret = 0;
- for(int i = 0; i < mFileNumbers.size() - 1; i++) {
+ for (int i = 0; i < mFileNumbers.size() - 1; i++) {
ret += getFile(mFileNumbers.get(i)).getBaseFile().length();
}
ret += mHistoryBuffer.dataSize();
if (mHistoryParcels != null) {
- for(int i = 0; i < mHistoryParcels.size(); i++) {
+ for (int i = 0; i < mHistoryParcels.size(); i++) {
ret += mHistoryParcels.get(i).dataSize();
}
}
return ret;
}
+
+ /**
+ * Enables/disables recording of history. When disabled, all "record*" calls are a no-op.
+ */
+ public void setHistoryRecordingEnabled(boolean enabled) {
+ mRecordingHistory = enabled;
+ }
+
+ /**
+ * Returns true if history recording is enabled.
+ */
+ public boolean isRecordingHistory() {
+ return mRecordingHistory;
+ }
+
+ /**
+ * Forces history recording regardless of charging state.
+ */
+ @VisibleForTesting
+ public void forceRecordAllHistory() {
+ mHaveBatteryLevel = true;
+ mRecordingHistory = true;
+ }
+
+ /**
+ * Starts a history buffer by recording the current wall-clock time.
+ */
+ public void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+ boolean reset) {
+ mRecordingHistory = true;
+ mHistoryCur.currentTime = mClock.currentTimeMillis();
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur,
+ reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME);
+ mHistoryCur.currentTime = 0;
+ }
+
+ /**
+ * Prepares to continue recording after restoring previous history from persistent storage.
+ */
+ public void continueRecordingHistory() {
+ if (mHistoryBuffer.dataPosition() <= 0 && mFileNumbers.size() <= 1) {
+ return;
+ }
+
+ mRecordingHistory = true;
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START);
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
+ }
+
+ /**
+ * Notes the current battery state to be reflected in the next written history item.
+ */
+ public void setBatteryState(boolean charging, int status, int level, int chargeUah) {
+ mHaveBatteryLevel = true;
+ setChargingState(charging);
+ mHistoryCur.batteryStatus = (byte) status;
+ mHistoryCur.batteryLevel = (byte) level;
+ mHistoryCur.batteryChargeUah = chargeUah;
+ }
+
+ /**
+ * Notes the current battery state to be reflected in the next written history item.
+ */
+ public void setBatteryState(int status, int level, int health, int plugType, int temperature,
+ int voltageMv, int chargeUah) {
+ mHaveBatteryLevel = true;
+ mHistoryCur.batteryStatus = (byte) status;
+ mHistoryCur.batteryLevel = (byte) level;
+ mHistoryCur.batteryHealth = (byte) health;
+ mHistoryCur.batteryPlugType = (byte) plugType;
+ mHistoryCur.batteryTemperature = (short) temperature;
+ mHistoryCur.batteryVoltage = (char) voltageMv;
+ mHistoryCur.batteryChargeUah = chargeUah;
+ }
+
+ /**
+ * Notes the current power plugged-in state to be reflected in the next written history item.
+ */
+ public void setPluggedInState(boolean pluggedIn) {
+ if (pluggedIn) {
+ mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ } else {
+ mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ }
+ }
+
+ /**
+ * Notes the current battery charging state to be reflected in the next written history item.
+ */
+ public void setChargingState(boolean charging) {
+ if (charging) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
+ } else {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
+ }
+ }
+
+ /**
+ * Records a history event with the given code, name and UID.
+ */
+ public void recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name,
+ int uid) {
+ mHistoryCur.eventCode = code;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.string = name;
+ mHistoryCur.eventTag.uid = uid;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a time change event.
+ */
+ public void recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
+ if (!mRecordingHistory) {
+ return;
+ }
+
+ mHistoryCur.currentTime = currentTimeMs;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur,
+ HistoryItem.CMD_CURRENT_TIME);
+ mHistoryCur.currentTime = 0;
+ }
+
+ /**
+ * Records a system shutdown event.
+ */
+ public void recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
+ if (!mRecordingHistory) {
+ return;
+ }
+
+ mHistoryCur.currentTime = currentTimeMs;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN);
+ mHistoryCur.currentTime = 0;
+ }
+
+ /**
+ * Records a battery state change event.
+ */
+ public void recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel,
+ boolean isPlugged) {
+ mHistoryCur.batteryLevel = (byte) batteryLevel;
+ setPluggedInState(isPlugged);
+ if (DEBUG) {
+ Slog.v(TAG, "Battery unplugged to: "
+ + Integer.toHexString(mHistoryCur.states));
+ }
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a history item with the amount of charge consumed by WiFi. Used on certain devices
+ * equipped with on-device power metering.
+ */
+ public void recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs,
+ double monitoredRailChargeMah) {
+ mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a wakelock start event.
+ */
+ public void recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName,
+ int uid) {
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = historyName;
+ mHistoryCur.wakelockTag.uid = uid;
+ recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG);
+ }
+
+ /**
+ * Updates the previous history event with a wakelock name and UID.
+ */
+ public boolean maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName,
+ int uid) {
+ if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) {
+ return false;
+ }
+ if (mHistoryLastWritten.wakelockTag != null) {
+ // We'll try to update the last tag.
+ mHistoryLastWritten.wakelockTag = null;
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = historyName;
+ mHistoryCur.wakelockTag.uid = uid;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+ return true;
+ }
+
+ /**
+ * Records an event when some state flag changes to true.
+ */
+ public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
+ mHistoryCur.states |= stateFlags;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records an event when some state flag changes to false.
+ */
+ public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
+ mHistoryCur.states &= ~stateFlags;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records an event when some state flags change to true and some to false.
+ */
+ public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags,
+ int stateStopFlags) {
+ mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records an event when some state2 flag changes to true.
+ */
+ public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
+ mHistoryCur.states2 |= stateFlags;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records an event when some state2 flag changes to false.
+ */
+ public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
+ mHistoryCur.states2 &= ~stateFlags;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records an wakeup event.
+ */
+ public void recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason) {
+ mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+ mHistoryCur.wakeReasonTag.string = reason;
+ mHistoryCur.wakeReasonTag.uid = 0;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a screen brightness change event.
+ */
+ public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs,
+ int brightnessBin) {
+ mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
+ | (brightnessBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a GNSS signal level change event.
+ */
+ public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs,
+ int signalLevel) {
+ mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+ | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a device idle mode change event.
+ */
+ public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) {
+ mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
+ | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a telephony state change event.
+ */
+ public void recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag,
+ int removeStateFlag, int state, int signalStrength) {
+ mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag;
+ if (state != -1) {
+ mHistoryCur.states =
+ (mHistoryCur.states & ~HistoryItem.STATE_PHONE_STATE_MASK)
+ | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
+ }
+ if (signalStrength != -1) {
+ mHistoryCur.states =
+ (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
+ | (signalStrength << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
+ }
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a data connection type change event.
+ */
+ public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs,
+ int dataConnectionType) {
+ mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_DATA_CONNECTION_MASK)
+ | (dataConnectionType << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a WiFi supplicant state change event.
+ */
+ public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs,
+ int supplState) {
+ mHistoryCur.states2 =
+ (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
+ | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records a WiFi signal strength change event.
+ */
+ public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs,
+ int strengthBin) {
+ mHistoryCur.states2 =
+ (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
+ | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Writes the current history item to history.
+ */
+ public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) {
+ if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
+ final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
+ final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
+ if (diffUptimeMs < (diffElapsedMs - 20)) {
+ final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
+ mHistoryAddTmp.setTo(mHistoryLastWritten);
+ mHistoryAddTmp.wakelockTag = null;
+ mHistoryAddTmp.wakeReasonTag = null;
+ mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+ mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+ writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
+ }
+ }
+ mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+ mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
+ mTrackRunningHistoryUptimeMs = uptimeMs;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ }
+
+ private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ if (!mHaveBatteryLevel || !mRecordingHistory) {
+ return;
+ }
+
+ final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
+ final int diffStates = mHistoryLastWritten.states ^ (cur.states & mActiveHistoryStates);
+ final int diffStates2 = mHistoryLastWritten.states2 ^ (cur.states2 & mActiveHistoryStates2);
+ final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states;
+ final int lastDiffStates2 = mHistoryLastWritten.states2 ^ mHistoryLastLastWritten.states2;
+ if (DEBUG) {
+ Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
+ + Integer.toHexString(diffStates) + " lastDiff="
+ + Integer.toHexString(lastDiffStates) + " diff2="
+ + Integer.toHexString(diffStates2) + " lastDiff2="
+ + Integer.toHexString(lastDiffStates2));
+ }
+ if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
+ && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
+ && (diffStates2 & lastDiffStates2) == 0
+ && (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence)
+ && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+ && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
+ && mHistoryLastWritten.stepDetails == null
+ && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
+ || cur.eventCode == HistoryItem.EVENT_NONE)
+ && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+ && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+ && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+ && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+ && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+ && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
+ // We can merge this new change in with the last one. Merging is
+ // allowed as long as only the states have changed, and within those states
+ // as long as no bit has changed both between now and the last entry, as
+ // well as the last entry and the one before it (so we capture any toggles).
+ if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
+ mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
+ mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
+ mHistoryBufferLastPos = -1;
+ elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
+ // If the last written history had a wakelock tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakelockTag != null) {
+ cur.wakelockTag = cur.localWakelockTag;
+ cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+ }
+ // If the last written history had a wake reason tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakeReasonTag != null) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+ }
+ // If the last written history had an event, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have an event.
+ if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
+ cur.eventCode = mHistoryLastWritten.eventCode;
+ cur.eventTag = cur.localEventTag;
+ cur.eventTag.setTo(mHistoryLastWritten.eventTag);
+ }
+ mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+ }
+ final int dataSize = mHistoryBuffer.dataSize();
+
+ if (dataSize >= mMaxHistoryBufferSize) {
+ if (mMaxHistoryBufferSize == 0) {
+ Slog.wtf(TAG, "mMaxHistoryBufferSize should not be zero when writing history");
+ mMaxHistoryBufferSize = 1024;
+ }
+
+ //open a new history file.
+ final long start = SystemClock.uptimeMillis();
+ writeHistory();
+ if (DEBUG) {
+ Slog.d(TAG, "addHistoryBufferLocked writeHistory took ms:"
+ + (SystemClock.uptimeMillis() - start));
+ }
+ startNextFile();
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
+ mHistoryBufferLastPos = -1;
+ mHistoryLastWritten.clear();
+ mHistoryLastLastWritten.clear();
+
+ // Mark every entry in the pool with a flag indicating that the tag
+ // has not yet been encountered while writing the current history buffer.
+ for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
+ entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
+ }
+ // Make a copy of mHistoryCur.
+ HistoryItem copy = new HistoryItem();
+ copy.setTo(cur);
+ // startRecordingHistory will reset mHistoryCur.
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
+ // Add the copy into history buffer.
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE);
+ return;
+ }
+
+ if (dataSize == 0) {
+ // The history is currently empty; we need it to start with a time stamp.
+ cur.currentTime = mClock.currentTimeMillis();
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_RESET);
+ }
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE);
+ }
+
+ private void writeHistoryItem(long elapsedRealtimeMs,
+ @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) {
+ if (mBatteryStatsHistoryIterator != null) {
+ throw new IllegalStateException("Can't do this while iterating history!");
+ }
+ mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
+ mHistoryLastLastWritten.setTo(mHistoryLastWritten);
+ final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
+ mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
+ mHistoryLastWritten.tagsFirstOccurrence = hasTags;
+ mHistoryLastWritten.states &= mActiveHistoryStates;
+ mHistoryLastWritten.states2 &= mActiveHistoryStates2;
+ writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
+ mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ cur.eventCode = HistoryItem.EVENT_NONE;
+ cur.eventTag = null;
+ cur.tagsFirstOccurrence = false;
+ if (DEBUG) {
+ Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ + " now " + mHistoryBuffer.dataPosition()
+ + " size is now " + mHistoryBuffer.dataSize());
+ }
+ }
+
+ /*
+ The history delta format uses flags to denote further data in subsequent ints in the parcel.
+
+ There is always the first token, which may contain the delta time, or an indicator of
+ the length of the time (int or long) following this token.
+
+ First token: always present,
+ 31 23 15 7 0
+ â–ˆM|L|K|J|I|H|G|Fâ–ˆE|D|C|B|A|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆ
+
+ T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately
+ follows containing the time, and 0x7ffff indicates a long immediately follows with the
+ delta time.
+ A: battery level changed and an int follows with battery data.
+ B: state changed and an int follows with state change data.
+ C: state2 has changed and an int follows with state2 change data.
+ D: wakelock/wakereason has changed and an wakelock/wakereason struct follows.
+ E: event data has changed and an event struct follows.
+ F: battery charge in coulombs has changed and an int with the charge follows.
+ G: state flag denoting that the mobile radio was active.
+ H: state flag denoting that the wifi radio was active.
+ I: state flag denoting that a wifi scan occurred.
+ J: state flag denoting that a wifi full lock was held.
+ K: state flag denoting that the gps was on.
+ L: state flag denoting that a wakelock was held.
+ M: state flag denoting that the cpu was running.
+
+ Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows
+ with the time delta.
+
+ Battery level int: if A in the first token is set,
+ 31 23 15 7 0
+ â–ˆL|L|L|L|L|L|L|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|V|V|V|V|V|V|Vâ–ˆV|V|V|V|V|V|V|Dâ–ˆ
+
+ D: indicates that extra history details follow.
+ V: the battery voltage.
+ T: the battery temperature.
+ L: the battery level (out of 100).
+
+ State change int: if B in the first token is set,
+ 31 23 15 7 0
+ â–ˆS|S|S|H|H|H|P|Pâ–ˆF|E|D|C|B| | |Aâ–ˆ | | | | | | | â–ˆ | | | | | | | â–ˆ
+
+ A: wifi multicast was on.
+ B: battery was plugged in.
+ C: screen was on.
+ D: phone was scanning for signal.
+ E: audio was on.
+ F: a sensor was active.
+
+ State2 change int: if C in the first token is set,
+ 31 23 15 7 0
+ â–ˆM|L|K|J|I|H|H|Gâ–ˆF|E|D|C| | | | â–ˆ | | | | | | | â–ˆ |B|B|B|A|A|A|Aâ–ˆ
+
+ A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}.
+ B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4.
+ C: a bluetooth scan was active.
+ D: the camera was active.
+ E: bluetooth was on.
+ F: a phone call was active.
+ G: the device was charging.
+ H: 2 bits indicating the device-idle (doze) state: off, light, full
+ I: the flashlight was on.
+ J: wifi was on.
+ K: wifi was running.
+ L: video was playing.
+ M: power save mode was on.
+
+ Wakelock/wakereason struct: if D in the first token is set,
+ Event struct: if E in the first token is set,
+ History step details struct: if D in the battery level int is set,
+
+ Battery charge int: if F in the first token is set, an int representing the battery charge
+ in coulombs follows.
+ */
+ /**
+ * Writes the delta between the previous and current history items into history buffer.
+ */
+ public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
+ if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
+ dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
+ cur.writeToParcel(dest, 0);
+ return;
+ }
+
+ final long deltaTime = cur.time - last.time;
+ final int lastBatteryLevelInt = buildBatteryLevelInt(last);
+ final int lastStateInt = buildStateInt(last);
+
+ int deltaTimeToken;
+ if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
+ deltaTimeToken = BatteryStatsHistory.DELTA_TIME_LONG;
+ } else if (deltaTime >= BatteryStatsHistory.DELTA_TIME_ABS) {
+ deltaTimeToken = BatteryStatsHistory.DELTA_TIME_INT;
+ } else {
+ deltaTimeToken = (int) deltaTime;
+ }
+ int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
+ final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
+ ? BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG : 0;
+ mLastHistoryStepLevel = cur.batteryLevel;
+ final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
+ final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
+ if (batteryLevelIntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG;
+ }
+ final int stateInt = buildStateInt(cur);
+ final boolean stateIntChanged = stateInt != lastStateInt;
+ if (stateIntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
+ }
+ final boolean state2IntChanged = cur.states2 != last.states2;
+ if (state2IntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ firstToken |= BatteryStatsHistory.DELTA_WAKELOCK_FLAG;
+ }
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ firstToken |= BatteryStatsHistory.DELTA_EVENT_FLAG;
+ }
+
+ final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
+ if (batteryChargeChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG;
+ }
+ dest.writeInt(firstToken);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTime=" + deltaTime);
+ }
+
+ if (deltaTimeToken >= BatteryStatsHistory.DELTA_TIME_INT) {
+ if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int) deltaTime);
+ dest.writeInt((int) deltaTime);
+ } else {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
+ dest.writeLong(deltaTime);
+ }
+ }
+ if (batteryLevelIntChanged) {
+ dest.writeInt(batteryLevelInt);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + cur.batteryLevel
+ + " batteryTemp=" + cur.batteryTemperature
+ + " batteryVolt=" + (int) cur.batteryVoltage);
+ }
+ }
+ if (stateIntChanged) {
+ dest.writeInt(stateInt);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + cur.batteryStatus
+ + " batteryHealth=" + cur.batteryHealth
+ + " batteryPlugType=" + cur.batteryPlugType
+ + " states=0x" + Integer.toHexString(cur.states));
+ }
+ }
+ if (state2IntChanged) {
+ dest.writeInt(cur.states2);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ int wakeLockIndex;
+ int wakeReasonIndex;
+ if (cur.wakelockTag != null) {
+ wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ }
+ } else {
+ wakeLockIndex = 0xffff;
+ }
+ if (cur.wakeReasonTag != null) {
+ wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ }
+ } else {
+ wakeReasonIndex = 0xffff;
+ }
+ dest.writeInt((wakeReasonIndex << 16) | wakeLockIndex);
+ if (cur.wakelockTag != null
+ && (wakeLockIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.wakelockTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
+ }
+ if (cur.wakeReasonTag != null
+ && (wakeReasonIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.wakeReasonTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
+ }
+ }
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ final int index = writeHistoryTag(cur.eventTag);
+ final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
+ dest.writeInt(codeAndIndex);
+ if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.eventTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
+ + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ + cur.eventTag.string);
+ }
+ }
+
+ cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails();
+ if (includeStepDetails != 0) {
+ cur.stepDetails.writeToParcel(dest);
+ }
+
+ if (batteryChargeChanged) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
+ dest.writeInt(cur.batteryChargeUah);
+ }
+ dest.writeDouble(cur.modemRailChargeMah);
+ dest.writeDouble(cur.wifiRailChargeMah);
+ }
+
+ private int buildBatteryLevelInt(HistoryItem h) {
+ return ((((int) h.batteryLevel) << 25) & 0xfe000000)
+ | ((((int) h.batteryTemperature) << 15) & 0x01ff8000)
+ | ((((int) h.batteryVoltage) << 1) & 0x00007ffe);
+ }
+
+ private int buildStateInt(HistoryItem h) {
+ int plugType = 0;
+ if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_AC) != 0) {
+ plugType = 1;
+ } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_USB) != 0) {
+ plugType = 2;
+ } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
+ plugType = 3;
+ }
+ return ((h.batteryStatus & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
+ | ((h.batteryHealth & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
+ | ((plugType & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
+ | (h.states & (~BatteryStatsHistory.STATE_BATTERY_MASK));
+ }
+
+ /**
+ * Returns the index for the specified tag. If this is the first time the tag is encountered
+ * while writing the current history buffer, the method returns
+ * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
+ */
+ private int writeHistoryTag(HistoryTag tag) {
+ if (tag.string == null) {
+ Slog.wtfStack(TAG, "writeHistoryTag called with null name");
+ }
+
+ final int stringLength = tag.string.length();
+ if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) {
+ Slog.e(TAG, "Long battery history tag: " + tag.string);
+ tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH);
+ }
+
+ Integer idxObj = mHistoryTagPool.get(tag);
+ int idx;
+ if (idxObj != null) {
+ idx = idxObj;
+ if ((idx & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ mHistoryTagPool.put(tag, idx & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
+ }
+ return idx;
+ } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
+ idx = mNextHistoryTagIdx;
+ HistoryTag key = new HistoryTag();
+ key.setTo(tag);
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(key, idx);
+ mNextHistoryTagIdx++;
+
+ mNumHistoryTagChars += stringLength + 1;
+ if (mHistoryTags != null) {
+ mHistoryTags.put(idx, key);
+ }
+ return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
+ } else {
+ // Tag pool overflow: include the tag itself in the parcel
+ return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
+ }
+ }
+
+ /**
+ * Don't allow any more batching in to the current history event.
+ */
+ public void commitCurrentHistoryBatchLocked() {
+ mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+ }
+
+ /**
+ * Saves the accumulated history buffer in the active file, see {@link #getActiveFile()} .
+ */
+ public void writeHistory() {
+ if (mActiveFile == null) {
+ Slog.w(TAG, "writeHistory: no history file associated with this instance");
+ return;
+ }
+
+ Parcel p = Parcel.obtain();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ writeHistoryBuffer(p);
+ if (DEBUG) {
+ Slog.d(TAG, "writeHistoryBuffer duration ms:"
+ + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
+ }
+ writeParcelToFileLocked(p, mActiveFile);
+ } finally {
+ p.recycle();
+ }
+ }
+
+ /**
+ * Reads history buffer from a persisted Parcel.
+ */
+ public void readHistoryBuffer(Parcel in) throws ParcelFormatException {
+ final int version = in.readInt();
+ if (version != BatteryStatsHistory.VERSION) {
+ Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
+ + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats");
+ return;
+ }
+
+ final long historyBaseTime = in.readLong();
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+
+ int bufSize = in.readInt();
+ int curPos = in.dataPosition();
+ if (bufSize >= (mMaxHistoryBufferSize * 100)) {
+ throw new ParcelFormatException(
+ "File corrupt: history data buffer too large " + bufSize);
+ } else if ((bufSize & ~3) != bufSize) {
+ throw new ParcelFormatException(
+ "File corrupt: history data buffer not aligned " + bufSize);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+ + " bytes at " + curPos);
+ }
+ mHistoryBuffer.appendFrom(in, curPos, bufSize);
+ in.setDataPosition(curPos + bufSize);
+ }
+
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** OLD mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ mHistoryBaseTimeMs = historyBaseTime;
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** NEW mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+
+ // We are just arbitrarily going to insert 1 minute from the sample of
+ // the last run until samples in this run.
+ if (mHistoryBaseTimeMs > 0) {
+ long oldnow = mClock.elapsedRealtime();
+ mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ }
+ }
+
+ private void writeHistoryBuffer(Parcel out) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** WRITING mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ sb.append(" mLastHistoryElapsedRealtimeMs: ");
+ TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ out.writeInt(BatteryStatsHistory.VERSION);
+ out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
+ out.writeInt(mHistoryBuffer.dataSize());
+ if (DEBUG) {
+ Slog.i(TAG, "***************** WRITING HISTORY: "
+ + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
+ }
+ out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+ }
+
+ private void writeParcelToFileLocked(Parcel p, AtomicFile file) {
+ FileOutputStream fos = null;
+ mWriteLock.lock();
+ try {
+ final long startTimeMs = SystemClock.uptimeMillis();
+ fos = file.startWrite();
+ fos.write(p.marshall());
+ fos.flush();
+ file.finishWrite(fos);
+ if (DEBUG) {
+ Slog.d(TAG, "writeParcelToFileLocked file:" + file.getBaseFile().getPath()
+ + " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
+ + " bytes:" + p.dataSize());
+ }
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ "batterystats", SystemClock.uptimeMillis() - startTimeMs);
+ } catch (IOException e) {
+ Slog.w(TAG, "Error writing battery statistics", e);
+ file.failWrite(fos);
+ } finally {
+ mWriteLock.unlock();
+ }
+ }
+
+ /**
+ * Returns the total number of history tags in the tag pool.
+ */
+ public int getHistoryStringPoolSize() {
+ return mHistoryTagPool.size();
+ }
+
+ /**
+ * Returns the total number of bytes occupied by the history tag pool.
+ */
+ public int getHistoryStringPoolBytes() {
+ return mNumHistoryTagChars;
+ }
+
+ /**
+ * Returns the string held by the requested history tag.
+ */
+ public String getHistoryTagPoolString(int index) {
+ ensureHistoryTagArray();
+ HistoryTag historyTag = mHistoryTags.get(index);
+ return historyTag != null ? historyTag.string : null;
+ }
+
+ /**
+ * Returns the UID held by the requested history tag.
+ */
+ public int getHistoryTagPoolUid(int index) {
+ ensureHistoryTagArray();
+ HistoryTag historyTag = mHistoryTags.get(index);
+ return historyTag != null ? historyTag.uid : Process.INVALID_UID;
+ }
+
+ private void ensureHistoryTagArray() {
+ if (mHistoryTags != null) {
+ return;
+ }
+
+ mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
+ for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
+ mHistoryTags.put(entry.getValue() & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG,
+ entry.getKey());
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index de8b414c4b78..1bf878cb9119 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -36,7 +36,6 @@ public class BatteryStatsHistoryIterator {
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) {
mBatteryStatsHistory = history;
- mBatteryStatsHistory.startIteratingHistory();
}
/**
@@ -231,4 +230,11 @@ public class BatteryStatsHistoryIterator {
out.batteryTemperature = (short) ((batteryLevelInt & 0x01ff8000) >>> 15);
out.batteryVoltage = (char) ((batteryLevelInt & 0x00007ffe) >>> 1);
}
+
+ /**
+ * Should be called when iteration is complete.
+ */
+ public void close() {
+ mBatteryStatsHistory.finishIteratingHistory();
+ }
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 4bc946e17d04..5498769fcf8f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -80,6 +80,10 @@ cc_library_shared {
"include",
],
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_target_shared",
+ ],
+
target: {
android: {
srcs: [
@@ -243,7 +247,6 @@ cc_library_shared {
],
shared_libs: [
- "android.media.audio.common.types-V1-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"audiopolicy-types-aidl-cpp",
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 0ebf2dddd7eb..5b946d5c8d3a 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -99,12 +99,13 @@ static void throwWriteRE(JNIEnv *env, binder_status_t status) {
jniThrowRuntimeException(env, "Could not write LongArrayMultiStateCounter to Parcel");
}
-#define THROW_ON_WRITE_ERROR(expr) \
- { \
- binder_status_t status = expr; \
- if (status != STATUS_OK) { \
- throwWriteRE(env, status); \
- } \
+#define THROW_AND_RETURN_ON_WRITE_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwWriteRE(env, status); \
+ return; \
+ } \
}
static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel,
@@ -114,14 +115,15 @@ static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, job
ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel));
uint16_t stateCount = counter->getStateCount();
- THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), stateCount));
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), stateCount));
// LongArrayMultiStateCounter has at least state 0
const std::vector<uint64_t> &anyState = counter->getCount(0);
- THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), anyState.size()));
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), anyState.size()));
for (battery::state_t state = 0; state < stateCount; state++) {
- THROW_ON_WRITE_ERROR(ndk::AParcel_writeVector(parcel.get(), counter->getCount(state)));
+ THROW_AND_RETURN_ON_WRITE_ERROR(
+ ndk::AParcel_writeVector(parcel.get(), counter->getCount(state)));
}
}
@@ -130,35 +132,37 @@ static void throwReadRE(JNIEnv *env, binder_status_t status) {
jniThrowRuntimeException(env, "Could not read LongArrayMultiStateCounter from Parcel");
}
-#define THROW_ON_READ_ERROR(expr) \
- { \
- binder_status_t status = expr; \
- if (status != STATUS_OK) { \
- throwReadRE(env, status); \
- } \
+#define THROW_AND_RETURN_ON_READ_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwReadRE(env, status); \
+ return 0L; \
+ } \
}
static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) {
ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel));
int32_t stateCount;
- THROW_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &stateCount));
+ THROW_AND_RETURN_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &stateCount));
int32_t arrayLength;
- THROW_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &arrayLength));
+ THROW_AND_RETURN_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &arrayLength));
- battery::LongArrayMultiStateCounter *counter =
- new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
+ auto counter = std::make_unique<battery::LongArrayMultiStateCounter>(stateCount,
+ std::vector<uint64_t>(
+ arrayLength));
std::vector<uint64_t> value;
value.reserve(arrayLength);
for (battery::state_t state = 0; state < stateCount; state++) {
- THROW_ON_READ_ERROR(ndk::AParcel_readVector(parcel.get(), &value));
+ THROW_AND_RETURN_ON_READ_ERROR(ndk::AParcel_readVector(parcel.get(), &value));
counter->setValue(state, value);
}
- return reinterpret_cast<jlong>(counter);
+ return reinterpret_cast<jlong>(counter.release());
}
static jint native_getStateCount(jlong nativePtr) {
diff --git a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
index 8c69d276fe6a..1712b3a8512b 100644
--- a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
@@ -109,12 +109,13 @@ static void throwWriteRE(JNIEnv *env, binder_status_t status) {
jniThrowRuntimeException(env, "Could not write LongMultiStateCounter to Parcel");
}
-#define THROW_ON_WRITE_ERROR(expr) \
- { \
- binder_status_t status = expr; \
- if (status != STATUS_OK) { \
- throwWriteRE(env, status); \
- } \
+#define THROW_AND_RETURN_ON_WRITE_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwWriteRE(env, status); \
+ return; \
+ } \
}
static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel,
@@ -123,10 +124,10 @@ static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, job
ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel));
uint16_t stateCount = counter->getStateCount();
- THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), stateCount));
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), stateCount));
for (battery::state_t state = 0; state < stateCount; state++) {
- THROW_ON_WRITE_ERROR(AParcel_writeInt64(parcel.get(), counter->getCount(state)));
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt64(parcel.get(), counter->getCount(state)));
}
}
@@ -135,29 +136,30 @@ static void throwReadRE(JNIEnv *env, binder_status_t status) {
jniThrowRuntimeException(env, "Could not read LongMultiStateCounter from Parcel");
}
-#define THROW_ON_READ_ERROR(expr) \
- { \
- binder_status_t status = expr; \
- if (status != STATUS_OK) { \
- throwReadRE(env, status); \
- } \
+#define THROW_AND_RETURN_ON_READ_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwReadRE(env, status); \
+ return 0L; \
+ } \
}
static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) {
ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel));
int32_t stateCount;
- THROW_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &stateCount));
+ THROW_AND_RETURN_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &stateCount));
- battery::LongMultiStateCounter *counter = new battery::LongMultiStateCounter(stateCount, 0);
+ auto counter = std::make_unique<battery::LongMultiStateCounter>(stateCount, 0);
for (battery::state_t state = 0; state < stateCount; state++) {
int64_t value;
- THROW_ON_READ_ERROR(AParcel_readInt64(parcel.get(), &value));
+ THROW_AND_RETURN_ON_READ_ERROR(AParcel_readInt64(parcel.get(), &value));
counter->setValue(state, value);
}
- return reinterpret_cast<jlong>(counter);
+ return reinterpret_cast<jlong>(counter.release());
}
static jint native_getStateCount(jlong nativePtr) {
diff --git a/core/proto/android/os/tombstone.proto b/core/proto/android/os/tombstone.proto
new file mode 100644
index 000000000000..2d3b1f3ae220
--- /dev/null
+++ b/core/proto/android/os/tombstone.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os;
+
+option java_multiple_files = true;
+
+message TombstoneWithHeadersProto {
+ // The original proto tombstone bytes.
+ // Proto located at: system/core/debuggerd/proto/tombstone.proto
+ optional bytes tombstone = 1;
+
+ // The count of dropped dropbox entries due to rate limiting.
+ optional int32 dropped_count = 2;
+} \ No newline at end of file
diff --git a/core/res/Android.bp b/core/res/Android.bp
index c42517d8a873..93ce7832824b 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -73,18 +73,18 @@ genrule {
":remote-color-resources-compile-colors",
],
out: ["remote-color-resources.apk"],
- cmd: "$(location aapt2) link -o $(out) --manifest $(in)"
+ cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
}
genrule {
name: "remote-color-resources-arsc",
srcs: [":remote-color-resources-apk"],
out: ["res/raw/remote_views_color_resources.arsc"],
- cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && "
- + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && "
- + "mkdir -p $$(dirname $(out)) && "
- + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && "
- + "echo 'Created $(out)'"
+ cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && " +
+ "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && " +
+ "mkdir -p $$(dirname $(out)) && " +
+ "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && " +
+ "echo 'Created $(out)'",
}
genrule {
@@ -95,11 +95,11 @@ genrule {
"remote_color_resources_res/symbols.xml",
],
out: ["remote_views_color_resources.zip"],
- cmd: "INPUTS=($(in)) && "
- + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
- + "mkdir -p $$RES_DIR/values && "
- + "cp $${INPUTS[1]} $$RES_DIR/values && "
- + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
+ cmd: "INPUTS=($(in)) && " +
+ "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && " +
+ "mkdir -p $$RES_DIR/values && " +
+ "cp $${INPUTS[1]} $$RES_DIR/values && " +
+ "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR",
}
android_app {
@@ -154,31 +154,21 @@ java_genrule {
cmd: "cp $(in) $(out)",
}
-// This logic can be removed once robolectric's transition to binary resources is complete
-filegroup {
- name: "robolectric_framework_raw_res_files",
- srcs: [
- "assets/**/*",
- "res/**/*",
- ":remote-color-resources-arsc",
- ],
-}
-
// Generate a text file containing a list of permissions that non-system apps
// are allowed to obtain.
genrule {
- name: "permission-list-normal",
- out: ["permission-list-normal.txt"],
- srcs: ["AndroidManifest.xml"],
- cmd: "cat $(in) " +
- // xmllint has trouble accessing attributes under the android namespace.
- // Strip these prefixes prior to processing with xmllint.
- " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
- " | $(location xmllint) /dev/stdin --xpath " +
- " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
- // The result of xmllint is name="value" pairs. Format these to just the
- // permission name, one per-line.
- " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
- " > $(out)",
- tools: ["xmllint"]
+ name: "permission-list-normal",
+ out: ["permission-list-normal.txt"],
+ srcs: ["AndroidManifest.xml"],
+ cmd: "cat $(in) " +
+ // xmllint has trouble accessing attributes under the android namespace.
+ // Strip these prefixes prior to processing with xmllint.
+ " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
+ " | $(location xmllint) /dev/stdin --xpath " +
+ " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
+ // The result of xmllint is name="value" pairs. Format these to just the
+ // permission name, one per-line.
+ " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
+ " > $(out)",
+ tools: ["xmllint"],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3fbd7973d81f..cd518cea8d87 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2975,7 +2975,7 @@
<permission android:name="android.permission.REAL_GET_TASKS"
android:protectionLevel="signature|privileged" />
- <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ <!-- @SystemApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
android:protectionLevel="signature|privileged|recents" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b59cf7fd3bcb..116b15051e7d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2392,9 +2392,6 @@
<!-- The list of supported dream complications -->
<integer-array name="config_supportedDreamComplications">
</integer-array>
- <!-- The list of dream complications which should be enabled by default -->
- <integer-array name="config_dreamComplicationsEnabledByDefault">
- </integer-array>
<!-- Are we allowed to dream while not plugged in? -->
<bool name="config_dreamsEnabledOnBattery">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7a2c866375d2..e5b1cf9be748 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3365,6 +3365,13 @@
<!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
<string name="not_selected">not selected</string>
+ <!-- Default label text used by accessibility for ratingbars. [CHAR LIMIT=NONE] -->
+ <string name ="rating_label">{ rating, plural,
+ =1 {One star out of {max}}
+ other {# stars out of {max}}
+ }
+ </string>
+
<!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
<string name="in_progress">in progress</string>
@@ -5752,7 +5759,7 @@
<!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
<string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
- \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device. Learn more
+ \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.
</string>
<!-- Privacy notice do not show [CHAR LIMIT=20] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 91f9bef740a9..d7836c12e9d6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -925,6 +925,7 @@
<java-symbol type="string" name="mobile_provisioning_apn" />
<java-symbol type="string" name="mobile_provisioning_url" />
<java-symbol type="string" name="quick_contacts_not_available" />
+ <java-symbol type="string" name="rating_label" />
<java-symbol type="string" name="reboot_to_update_package" />
<java-symbol type="string" name="reboot_to_update_prepare" />
<java-symbol type="string" name="reboot_to_update_title" />
@@ -2228,7 +2229,6 @@
<java-symbol type="string" name="config_dreamsDefaultComponent" />
<java-symbol type="bool" name="config_dreamsOnlyEnabledForSystemUser" />
<java-symbol type="array" name="config_supportedDreamComplications" />
- <java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
<java-symbol type="string" name="config_loggable_dream_prefix" />
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 9eb4ccb2663b..a22dd1c63ddf 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -18,6 +18,8 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
import androidx.test.filters.SmallTest;
@@ -149,4 +151,14 @@ public class LongArrayMultiStateCounterTest {
container.getValues(counts);
assertThat(counts).isEqualTo(expected);
}
+
+ @Test
+ public void createFromBadParcel() {
+ // Check we don't crash the runtime if the Parcel data is bad (b/243434675).
+ Parcel parcel = Parcel.obtain();
+ parcel.writeInt(2);
+ parcel.setDataPosition(0);
+ assertThrows(RuntimeException.class,
+ () -> LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
index e717ebbc997c..fc86ebe1c10e 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
@@ -18,6 +18,8 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
import androidx.test.filters.SmallTest;
@@ -198,4 +200,14 @@ public class LongMultiStateCounterTest {
assertThat(newCounter.getCount(0)).isEqualTo(116);
}
+
+ @Test
+ public void createFromBadParcel() {
+ // Check we don't crash the runtime if the Parcel data is bad (b/243434675).
+ Parcel parcel = Parcel.obtain();
+ parcel.writeInt(13);
+ parcel.setDataPosition(0);
+ assertThrows(RuntimeException.class,
+ () -> LongMultiStateCounter.CREATOR.createFromParcel(parcel));
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 748032b9db62..6aedcd493f5c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2671,6 +2671,12 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "390947100": {
+ "message": "Screenshotting %s [%s]",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"397382873": {
"message": "Moving to PAUSED: %s %s",
"level": "VERBOSE",
@@ -4189,6 +4195,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/InputMonitor.java"
},
+ "2004282287": {
+ "message": "Override sync-method for %s because seamless rotating",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"2010476671": {
"message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 48aecd61fc19..d0327159b04d 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -28,7 +28,7 @@ import java.util.Objects;
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
- * line-break property</a> for more information.</p>
+ * line-break property</a> for more information.
*/
public final class LineBreakConfig {
@@ -72,7 +72,7 @@ public final class LineBreakConfig {
*
* <p>Support for this line-break word style depends on locale. If the
* current locale does not support phrase-based text wrapping, this setting
- * has no effect.</p>
+ * has no effect.
*/
public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1;
@@ -194,7 +194,7 @@ public final class LineBreakConfig {
* Constructor with line-break parameters.
*
* <p>Use {@link LineBreakConfig.Builder} to create the
- * {@code LineBreakConfig} instance.</p>
+ * {@code LineBreakConfig} instance.
*/
private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
@LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 6bfb16a3c22d..f24401f0cd53 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -20,21 +20,26 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
+import static androidx.window.util.ExtensionHelper.isZero;
import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
-import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.Application;
import android.app.WindowConfiguration;
+import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.window.WindowContext;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiContext;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
@@ -58,11 +63,14 @@ import java.util.function.Consumer;
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private static final String TAG = "SampleExtension";
- private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
+ private final Map<Context, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
new ArrayMap<>();
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
+ private final Map<IBinder, WindowContextConfigListener> mWindowContextConfigListeners =
+ new ArrayMap<>();
+
public WindowLayoutComponentImpl(@NonNull Context context) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
@@ -78,14 +86,42 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
* @param activity hosting a {@link android.view.Window}
* @param consumer interested in receiving updates to {@link WindowLayoutInfo}
*/
+ @Override
public void addWindowLayoutInfoListener(@NonNull Activity activity,
@NonNull Consumer<WindowLayoutInfo> consumer) {
+ addWindowLayoutInfoListener((Context) activity, consumer);
+ }
+
+ /**
+ * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
+ * as a parameter.
+ */
+ // TODO(b/204073440): Add @Override to hook the API in WM extensions library.
+ public void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ if (mWindowLayoutChangeListeners.containsKey(context)
+ || mWindowLayoutChangeListeners.containsValue(consumer)) {
+ // Early return if the listener or consumer has been registered.
+ return;
+ }
+ if (!context.isUiContext()) {
+ throw new IllegalArgumentException("Context must be a UI Context, which should be"
+ + " an Activity or a WindowContext");
+ }
mFoldingFeatureProducer.getData((features) -> {
// Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer.
- WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, features);
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
consumer.accept(newWindowLayout);
});
- mWindowLayoutChangeListeners.put(activity, consumer);
+ mWindowLayoutChangeListeners.put(context, consumer);
+
+ if (context instanceof WindowContext) {
+ final IBinder windowContextToken = context.getWindowContextToken();
+ final WindowContextConfigListener listener =
+ new WindowContextConfigListener(windowContextToken);
+ context.registerComponentCallbacks(listener);
+ mWindowContextConfigListeners.put(windowContextToken, listener);
+ }
}
/**
@@ -93,18 +129,30 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*
* @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
*/
+ @Override
public void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) {
+ for (Context context : mWindowLayoutChangeListeners.keySet()) {
+ if (!mWindowLayoutChangeListeners.get(context).equals(consumer)) {
+ continue;
+ }
+ if (context instanceof WindowContext) {
+ final IBinder token = context.getWindowContextToken();
+ context.unregisterComponentCallbacks(mWindowContextConfigListeners.get(token));
+ mWindowContextConfigListeners.remove(token);
+ }
+ break;
+ }
mWindowLayoutChangeListeners.values().remove(consumer);
}
@NonNull
- Set<Activity> getActivitiesListeningForLayoutChanges() {
+ Set<Context> getContextsListeningForLayoutChanges() {
return mWindowLayoutChangeListeners.keySet();
}
private boolean isListeningForLayoutChanges(IBinder token) {
- for (Activity activity: getActivitiesListeningForLayoutChanges()) {
- if (token.equals(activity.getWindow().getAttributes().token)) {
+ for (Context context: getContextsListeningForLayoutChanges()) {
+ if (token.equals(Context.getToken(context))) {
return true;
}
}
@@ -138,10 +186,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
- for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+ for (Context context : getContextsListeningForLayoutChanges()) {
// Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer.
- Consumer<WindowLayoutInfo> layoutConsumer = mWindowLayoutChangeListeners.get(activity);
- WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, storedFeatures);
+ Consumer<WindowLayoutInfo> layoutConsumer = mWindowLayoutChangeListeners.get(context);
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, storedFeatures);
layoutConsumer.accept(newWindowLayout);
}
}
@@ -149,11 +197,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
/**
* Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a
* valid state is found.
- * @param activity a proxy for the {@link android.view.Window} that contains the
+ * @param context a proxy for the {@link android.view.Window} that contains the
+ * {@link DisplayFeature}.
*/
- private WindowLayoutInfo getWindowLayoutInfo(
- @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) {
- List<DisplayFeature> displayFeatureList = getDisplayFeatures(activity, storedFeatures);
+ private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context,
+ List<CommonFoldingFeature> storedFeatures) {
+ List<DisplayFeature> displayFeatureList = getDisplayFeatures(context, storedFeatures);
return new WindowLayoutInfo(displayFeatureList);
}
@@ -170,18 +219,18 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
* bounds are not valid, constructing a {@link FoldingFeature} will throw an
* {@link IllegalArgumentException} since this can cause negative UI effects down stream.
*
- * @param activity a proxy for the {@link android.view.Window} that contains the
+ * @param context a proxy for the {@link android.view.Window} that contains the
* {@link DisplayFeature}.
* are within the {@link android.view.Window} of the {@link Activity}
*/
private List<DisplayFeature> getDisplayFeatures(
- @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) {
+ @NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
List<DisplayFeature> features = new ArrayList<>();
- if (!shouldReportDisplayFeatures(activity)) {
+ if (!shouldReportDisplayFeatures(context)) {
return features;
}
- int displayId = activity.getDisplay().getDisplayId();
+ int displayId = context.getDisplay().getDisplayId();
for (CommonFoldingFeature baseFeature : storedFeatures) {
Integer state = convertToExtensionState(baseFeature.getState());
if (state == null) {
@@ -189,9 +238,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(activity, featureRect);
+ transformToWindowSpaceRect(context, featureRect);
- if (!isRectZero(featureRect)) {
+ if (!isZero(featureRect)) {
// TODO(b/228641877): Remove guarding when fixed.
features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
@@ -203,15 +252,21 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
* Checks whether display features should be reported for the activity.
* TODO(b/238948678): Support reporting display features in all windowing modes.
*/
- private boolean shouldReportDisplayFeatures(@NonNull Activity activity) {
- int displayId = activity.getDisplay().getDisplayId();
+ private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
+ int displayId = context.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
// Display features are not supported on secondary displays.
return false;
}
- final int taskWindowingMode = ActivityClient.getInstance().getTaskWindowingMode(
- activity.getActivityToken());
- if (taskWindowingMode == -1) {
+ final int windowingMode;
+ if (context instanceof Activity) {
+ windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
+ context.getActivityToken());
+ } else {
+ windowingMode = context.getResources().getConfiguration().windowConfiguration
+ .getWindowingMode();
+ }
+ if (windowingMode == -1) {
// If we cannot determine the task windowing mode for any reason, it is likely that we
// won't be able to determine its position correctly as well. DisplayFeatures' bounds
// in this case can't be computed correctly, so we should skip.
@@ -219,36 +274,43 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
- return !WindowConfiguration.inMultiWindowMode(taskWindowingMode);
+ return !WindowConfiguration.inMultiWindowMode(windowingMode);
}
- /**
- * Returns {@link true} if a {@link Rect} has zero width and zero height,
- * {@code false} otherwise.
- */
- private boolean isRectZero(Rect rect) {
- return rect.width() == 0 && rect.height() == 0;
+ private void onDisplayFeaturesChangedIfListening(@NonNull IBinder token) {
+ if (isListeningForLayoutChanges(token)) {
+ mFoldingFeatureProducer.getData(
+ WindowLayoutComponentImpl.this::onDisplayFeaturesChanged);
+ }
}
private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
super.onActivityCreated(activity, savedInstanceState);
- onDisplayFeaturesChangedIfListening(activity);
+ onDisplayFeaturesChangedIfListening(activity.getActivityToken());
}
@Override
public void onActivityConfigurationChanged(Activity activity) {
super.onActivityConfigurationChanged(activity);
- onDisplayFeaturesChangedIfListening(activity);
+ onDisplayFeaturesChangedIfListening(activity.getActivityToken());
+ }
+ }
+
+ private final class WindowContextConfigListener implements ComponentCallbacks {
+ final IBinder mToken;
+
+ WindowContextConfigListener(IBinder token) {
+ mToken = token;
}
- private void onDisplayFeaturesChangedIfListening(Activity activity) {
- IBinder token = activity.getWindow().getAttributes().token;
- if (token == null || isListeningForLayoutChanges(token)) {
- mFoldingFeatureProducer.getData(
- WindowLayoutComponentImpl.this::onDisplayFeaturesChanged);
- }
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ onDisplayFeaturesChangedIfListening(mToken);
}
+
+ @Override
+ public void onLowMemory() {}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
index 0da44ac36a6e..cbaa27712015 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -16,6 +16,7 @@
package androidx.window.util;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import java.util.LinkedHashSet;
@@ -25,25 +26,45 @@ import java.util.function.Consumer;
/**
* Base class that provides the implementation for the callback mechanism of the
- * {@link DataProducer} API.
+ * {@link DataProducer} API. This class is thread safe for adding, removing, and notifying
+ * consumers.
*
* @param <T> The type of data this producer returns through {@link DataProducer#getData}.
*/
public abstract class BaseDataProducer<T> implements DataProducer<T> {
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
+ /**
+ * Adds a callback to the set of callbacks listening for data. Data is delivered through
+ * {@link BaseDataProducer#notifyDataChanged(Object)}. This method is thread safe. Callers
+ * should ensure that callbacks are thread safe.
+ * @param callback that will receive data from the producer.
+ */
@Override
public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
- mCallbacks.add(callback);
- Optional<T> currentData = getCurrentData();
- currentData.ifPresent(callback);
- onListenersChanged(mCallbacks);
+ synchronized (mLock) {
+ mCallbacks.add(callback);
+ Optional<T> currentData = getCurrentData();
+ currentData.ifPresent(callback);
+ onListenersChanged(mCallbacks);
+ }
}
+ /**
+ * Removes a callback to the set of callbacks listening for data. This method is thread safe
+ * for adding.
+ * @param callback that was registered in
+ * {@link BaseDataProducer#addDataChangedCallback(Consumer)}.
+ */
@Override
public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
- mCallbacks.remove(callback);
- onListenersChanged(mCallbacks);
+ synchronized (mLock) {
+ mCallbacks.remove(callback);
+ onListenersChanged(mCallbacks);
+ }
}
protected void onListenersChanged(Set<Consumer<T>> callbacks) {}
@@ -56,11 +77,14 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> {
/**
* Called to notify all registered consumers that the data provided
- * by {@link DataProducer#getData} has changed.
+ * by {@link DataProducer#getData} has changed. Calls to this are thread save but callbacks need
+ * to ensure thread safety.
*/
protected void notifyDataChanged(T value) {
- for (Consumer<T> callback : mCallbacks) {
- callback.accept(value);
+ synchronized (mLock) {
+ for (Consumer<T> callback : mCallbacks) {
+ callback.accept(value);
+ }
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index 2a593f15a9de..31bf96313a95 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -21,14 +21,15 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import android.app.Activity;
+import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.WindowManager;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import androidx.annotation.UiContext;
/**
* Util class for both Sidecar and Extensions.
@@ -86,12 +87,9 @@ public final class ExtensionHelper {
}
/** Transforms rectangle from absolute coordinate space to the window coordinate space. */
- public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
- Rect windowRect = getWindowBounds(activity);
- if (windowRect == null) {
- inOutRect.setEmpty();
- return;
- }
+ public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
+ Rect inOutRect) {
+ Rect windowRect = getWindowBounds(context);
if (!Rect.intersects(inOutRect, windowRect)) {
inOutRect.setEmpty();
return;
@@ -103,9 +101,9 @@ public final class ExtensionHelper {
/**
* Gets the current window bounds in absolute coordinates.
*/
- @Nullable
- private static Rect getWindowBounds(@NonNull Activity activity) {
- return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
+ @NonNull
+ private static Rect getWindowBounds(@NonNull @UiContext Context context) {
+ return context.getSystemService(WindowManager.class).getCurrentWindowMetrics().getBounds();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 5bf88b119661..aeaf6eda9809 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -111,6 +111,9 @@ public class BubbleStackView extends FrameLayout
public static final boolean HOME_GESTURE_ENABLED =
SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
+ public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
+ SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index ae434bcec6c4..b91062f891e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.animation;
import static android.view.View.LAYOUT_DIRECTION_RTL;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
import static com.android.wm.shell.bubbles.BubbleStackView.HOME_GESTURE_ENABLED;
import android.content.res.Resources;
@@ -366,6 +367,7 @@ public class ExpandedAnimationController
mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+ mMagnetizedBubbleDraggingOut.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_BUBBLE);
}
private void springBubbleTo(View bubble, float x, float y) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 4e2cbfd82fcc..961722ba9bc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.bubbles.animation;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
import android.content.ContentResolver;
import android.content.res.Resources;
@@ -1028,6 +1029,7 @@ public class StackAnimationController extends
};
mMagnetizedStack.setHapticsEnabled(true);
mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+ mMagnetizedStack.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_BUBBLE);
}
final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index ceaa64ef8290..7a736ccab5d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -85,6 +85,7 @@ import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.util.Optional;
@@ -294,25 +295,33 @@ public abstract class WMShellBaseModule {
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
@DynamicOverride
- abstract FullscreenTaskListener optionalFullscreenTaskListener();
+ abstract FullscreenTaskListener<?> optionalFullscreenTaskListener();
@WMSingleton
@Provides
- static FullscreenTaskListener provideFullscreenTaskListener(
- @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
+ static FullscreenTaskListener<?> provideFullscreenTaskListener(
+ @DynamicOverride Optional<FullscreenTaskListener<?>> fullscreenTaskListener,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
- Optional<RecentTasksController> recentTasksOptional) {
+ Optional<RecentTasksController> recentTasksOptional,
+ Optional<WindowDecorViewModel<?>> windowDecorViewModelOptional) {
if (fullscreenTaskListener.isPresent()) {
return fullscreenTaskListener.get();
} else {
return new FullscreenTaskListener(shellInit, shellTaskOrganizer, syncQueue,
- recentTasksOptional);
+ recentTasksOptional, windowDecorViewModelOptional);
}
}
//
+ // Window Decoration
+ //
+
+ @BindsOptionalOf
+ abstract WindowDecorViewModel<?> optionalWindowDecorViewModel();
+
+ //
// Unfold transition
//
@@ -680,7 +689,7 @@ public abstract class WMShellBaseModule {
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener fullscreenTaskListener,
+ FullscreenTaskListener<?> fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 4fe32556a94d..c64d1134a4ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -55,6 +55,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
+import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -233,6 +234,7 @@ public abstract class WMShellModule {
ShellInit shellInit,
Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel,
+ FullscreenTaskListener<?> fullscreenTaskListener,
FreeformTaskListener<?> freeformTaskListener) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
@@ -240,7 +242,7 @@ public abstract class WMShellModule {
? shellInit
: null;
return new FreeformTaskTransitionHandler(init, transitions,
- windowDecorViewModel, freeformTaskListener);
+ windowDecorViewModel, fullscreenTaskListener, freeformTaskListener);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index ab66107399c6..8dcdda1895e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -187,12 +187,14 @@ public class FreeformTaskListener<T extends AutoCloseable>
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- T windowDecor = mWindowDecorOfVanishedTasks.get(taskInfo.taskId);
- mWindowDecorOfVanishedTasks.remove(taskInfo.taskId);
+ T windowDecor;
final State<T> state = mTasks.get(taskInfo.taskId);
if (state != null) {
- windowDecor = windowDecor == null ? state.mWindowDecoration : windowDecor;
+ windowDecor = state.mWindowDecoration;
state.mWindowDecoration = null;
+ } else {
+ windowDecor =
+ mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
}
mWindowDecorationViewModel.setupWindowDecorationForTransition(
taskInfo, startT, finishT, windowDecor);
@@ -231,7 +233,8 @@ public class FreeformTaskListener<T extends AutoCloseable>
if (mWindowDecorOfVanishedTasks.size() == 0) {
return;
}
- Log.w(TAG, "Clearing window decors of vanished tasks. There could be visual defects "
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Clearing window decors of vanished tasks. There could be visual defects "
+ "if any of them is used later in transitions.");
for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) {
releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index a1e9f938d280..30f625e24118 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -32,6 +32,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -50,6 +51,7 @@ public class FreeformTaskTransitionHandler
private final Transitions mTransitions;
private final FreeformTaskListener<?> mFreeformTaskListener;
+ private final FullscreenTaskListener<?> mFullscreenTaskListener;
private final WindowDecorViewModel<?> mWindowDecorViewModel;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -58,8 +60,10 @@ public class FreeformTaskTransitionHandler
ShellInit shellInit,
Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel,
+ FullscreenTaskListener<?> fullscreenTaskListener,
FreeformTaskListener<?> freeformTaskListener) {
mTransitions = transitions;
+ mFullscreenTaskListener = fullscreenTaskListener;
mFreeformTaskListener = freeformTaskListener;
mWindowDecorViewModel = windowDecorViewModel;
if (shellInit != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -150,10 +154,16 @@ public class FreeformTaskTransitionHandler
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- return false;
+ switch (change.getTaskInfo().getWindowingMode()){
+ case WINDOWING_MODE_FREEFORM:
+ mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
+ break;
+ case WINDOWING_MODE_FULLSCREEN:
+ mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
+ break;
+ default:
+ return false;
}
- mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
// Intercepted transition to manage the window decorations. Let other handlers animate.
return false;
@@ -164,15 +174,22 @@ public class FreeformTaskTransitionHandler
ArrayList<AutoCloseable> windowDecors,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- return false;
+ final AutoCloseable windowDecor;
+ switch (change.getTaskInfo().getWindowingMode()) {
+ case WINDOWING_MODE_FREEFORM:
+ windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
+ startT, finishT);
+ break;
+ case WINDOWING_MODE_FULLSCREEN:
+ windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
+ startT, finishT);
+ break;
+ default:
+ windowDecor = null;
}
- final AutoCloseable windowDecor =
- mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(), startT, finishT);
if (windowDecor != null) {
windowDecors.add(windowDecor);
}
-
// Intercepted transition to manage the window decorations. Let other handlers animate.
return false;
}
@@ -197,24 +214,29 @@ public class FreeformTaskTransitionHandler
}
boolean handled = false;
+ boolean adopted = false;
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (type == Transitions.TRANSIT_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
handled = true;
windowDecor = mFreeformTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
- // TODO(b/235638450): Let fullscreen task listener adopt the window decor.
+ adopted = mFullscreenTaskListener.adoptWindowDecoration(change,
+ startT, finishT, windowDecor);
}
if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
handled = true;
- // TODO(b/235638450): Let fullscreen task listener transfer the window decor.
- mFreeformTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor);
+ windowDecor = mFullscreenTaskListener.giveWindowDecoration(
+ change.getTaskInfo(), startT, finishT);
+ adopted = mFreeformTaskListener.adoptWindowDecoration(change,
+ startT, finishT, windowDecor);
}
- releaseWindowDecor(windowDecor);
-
+ if (!adopted) {
+ releaseWindowDecor(windowDecor);
+ }
return handled;
}
@@ -225,7 +247,7 @@ public class FreeformTaskTransitionHandler
releaseWindowDecor(windowDecor);
}
mFreeformTaskListener.onTaskTransitionFinished();
- // TODO(b/235638450): Dispatch it to fullscreen task listener.
+ mFullscreenTaskListener.onTaskTransitionFinished();
finishCallback.onTransitionFinished(null, null);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 0ba4afc24c45..0d75bc451b72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -16,16 +16,21 @@
package com.android.wm.shell.fullscreen;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Point;
-import android.util.Slog;
+import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
+import android.window.TransitionInfo;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -34,36 +39,49 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
import java.util.Optional;
/**
* Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
+ * @param <T> the type of window decoration instance
*/
-public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
+public class FullscreenTaskListener<T extends AutoCloseable>
+ implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final SyncTransactionQueue mSyncQueue;
- private final Optional<RecentTasksController> mRecentTasksOptional;
- private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
+ private final SparseArray<State<T>> mTasks = new SparseArray<>();
+ private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();
+ private static class State<T extends AutoCloseable> {
+ RunningTaskInfo mTaskInfo;
+ SurfaceControl mLeash;
+ T mWindowDecoration;
+ }
+ private final SyncTransactionQueue mSyncQueue;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final Optional<WindowDecorViewModel<T>> mWindowDecorViewModelOptional;
/**
* This constructor is used by downstream products.
*/
public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
- this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty());
+ this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty(),
+ Optional.empty());
}
public FullscreenTaskListener(ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
- Optional<RecentTasksController> recentTasksOptional) {
+ Optional<RecentTasksController> recentTasksOptional,
+ Optional<WindowDecorViewModel<T>> windowDecorViewModelOptional) {
mShellTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mRecentTasksOptional = recentTasksOptional;
+ mWindowDecorViewModelOptional = windowDecorViewModelOptional;
// Note: Some derivative FullscreenTaskListener implementations do not use ShellInit
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
@@ -76,55 +94,204 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
- if (mDataByTaskId.get(taskInfo.taskId) != null) {
+ if (mTasks.get(taskInfo.taskId) != null) {
throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
- mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- mSyncQueue.runInSync(t -> {
- // Reset several properties back to fullscreen (PiP, for example, leaves all these
- // properties in a bad state).
- t.setWindowCrop(leash, null);
- t.setPosition(leash, positionInParent.x, positionInParent.y);
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- });
+ final State<T> state = new State();
+ state.mLeash = leash;
+ state.mTaskInfo = taskInfo;
+ mTasks.put(taskInfo.taskId, state);
updateRecentsForVisibleFullscreenTask(taskInfo);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ state.mWindowDecoration =
+ mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo,
+ leash, t, t);
+ t.apply();
+ } else {
+ mSyncQueue.runInSync(t -> {
+ // Reset several properties back to fullscreen (PiP, for example, leaves all these
+ // properties in a bad state).
+ t.setWindowCrop(leash, null);
+ t.setPosition(leash, positionInParent.x, positionInParent.y);
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ });
+ }
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
-
+ final State<T> state = mTasks.get(taskInfo.taskId);
+ final Point oldPositionInParent = state.mTaskInfo.positionInParent;
+ state.mTaskInfo = taskInfo;
+ if (state.mWindowDecoration != null) {
+ mWindowDecorViewModelOptional.get().onTaskInfoChanged(
+ state.mTaskInfo, state.mWindowDecoration);
+ }
updateRecentsForVisibleFullscreenTask(taskInfo);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- final TaskData data = mDataByTaskId.get(taskInfo.taskId);
- final Point positionInParent = taskInfo.positionInParent;
- if (!positionInParent.equals(data.positionInParent)) {
- data.positionInParent.set(positionInParent.x, positionInParent.y);
+ final Point positionInParent = state.mTaskInfo.positionInParent;
+ if (!oldPositionInParent.equals(state.mTaskInfo.positionInParent)) {
mSyncQueue.runInSync(t -> {
- t.setPosition(data.surface, positionInParent.x, positionInParent.y);
+ t.setPosition(state.mLeash, positionInParent.x, positionInParent.y);
});
}
}
@Override
- public void onTaskVanished(RunningTaskInfo taskInfo) {
- if (mDataByTaskId.get(taskInfo.taskId) == null) {
- Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ final State<T> state = mTasks.get(taskInfo.taskId);
+ if (state == null) {
+ // This is possible if the transition happens before this method.
return;
}
-
- mDataByTaskId.remove(taskInfo.taskId);
-
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId);
+ mTasks.remove(taskInfo.taskId);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ // Save window decorations of closing tasks so that we can hand them over to the
+ // transition system if this method happens before the transition. In case where the
+ // transition didn't happen, it'd be cleared when the next transition finished.
+ if (state.mWindowDecoration != null) {
+ mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration);
+ }
+ return;
+ }
+ releaseWindowDecor(state.mWindowDecoration);
+ }
+
+ /**
+ * Creates a window decoration for a transition.
+ *
+ * @param change the change of this task transition that needs to have the task layer as the
+ * leash
+ */
+ public void createWindowDecoration(TransitionInfo.Change change,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
+ if (!mWindowDecorViewModelOptional.isPresent()
+ || !shouldShowWindowDecor(state.mTaskInfo)) {
+ return;
+ }
+
+ state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ state.mTaskInfo, state.mLeash, startT, finishT);
+ }
+
+ /**
+ * Adopt the incoming window decoration and lets the window decoration prepare for a transition.
+ *
+ * @param change the change of this task transition that needs to have the task layer as the
+ * leash
+ * @param startT the start transaction of this transition
+ * @param finishT the finish transaction of this transition
+ * @param windowDecor the window decoration to adopt
+ * @return {@code true} if it adopts the window decoration; {@code false} otherwise
+ */
+ public boolean adoptWindowDecoration(
+ TransitionInfo.Change change,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT,
+ @Nullable AutoCloseable windowDecor) {
+ if (!mWindowDecorViewModelOptional.isPresent()
+ || !shouldShowWindowDecor(change.getTaskInfo())) {
+ return false;
+ }
+ final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
+ state.mWindowDecoration = mWindowDecorViewModelOptional.get().adoptWindowDecoration(
+ windowDecor);
+ if (state.mWindowDecoration != null) {
+ mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
+ state.mTaskInfo, startT, finishT, state.mWindowDecoration);
+ return true;
+ } else {
+ state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ state.mTaskInfo, state.mLeash, startT, finishT);
+ return false;
+ }
+ }
+
+ /**
+ * Clear window decors of vanished tasks.
+ */
+ public void onTaskTransitionFinished() {
+ if (mWindowDecorOfVanishedTasks.size() == 0) {
+ return;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Clearing window decors of vanished tasks. There could be visual defects "
+ + "if any of them is used later in transitions.");
+ for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) {
+ releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i));
+ }
+ mWindowDecorOfVanishedTasks.clear();
+ }
+
+ /**
+ * Gives out the ownership of the task's window decoration. The given task is leaving (of has
+ * left) this task listener. This is the transition system asking for the ownership.
+ *
+ * @param taskInfo the maximizing task
+ * @return the window decor of the maximizing task if any
+ */
+ public T giveWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ T windowDecor;
+ final State<T> state = mTasks.get(taskInfo.taskId);
+ if (state != null) {
+ windowDecor = state.mWindowDecoration;
+ state.mWindowDecoration = null;
+ } else {
+ windowDecor =
+ mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
+ }
+ if (mWindowDecorViewModelOptional.isPresent()) {
+ mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
+ taskInfo, startT, finishT, windowDecor);
+ }
+
+ return windowDecor;
+ }
+
+ private State<T> createOrUpdateTaskState(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl leash) {
+ State<T> state = mTasks.get(taskInfo.taskId);
+ if (state != null) {
+ updateTaskInfo(taskInfo);
+ return state;
+ }
+
+ state = new State<T>();
+ state.mTaskInfo = taskInfo;
+ state.mLeash = leash;
+ mTasks.put(taskInfo.taskId, state);
+
+ return state;
+ }
+
+ private State<T> updateTaskInfo(ActivityManager.RunningTaskInfo taskInfo) {
+ final State<T> state = mTasks.get(taskInfo.taskId);
+ state.mTaskInfo = taskInfo;
+ return state;
+ }
+
+ private void releaseWindowDecor(T windowDecor) {
+ try {
+ windowDecor.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to release window decoration.", e);
+ }
}
private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) {
@@ -148,17 +315,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
}
private SurfaceControl findTaskSurface(int taskId) {
- if (!mDataByTaskId.contains(taskId)) {
+ if (!mTasks.contains(taskId)) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
- return mDataByTaskId.get(taskId).surface;
+ return mTasks.get(taskId).mLeash;
}
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + this);
- pw.println(innerPrefix + mDataByTaskId.size() + " Tasks");
+ pw.println(innerPrefix + mTasks.size() + " Tasks");
}
@Override
@@ -166,16 +333,10 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
- /**
- * Per-task data for each managed task.
- */
- private static class TaskData {
- public final SurfaceControl surface;
- public final Point positionInParent;
-
- public TaskData(SurfaceControl surface, Point positionInParent) {
- this.surface = surface;
- this.positionInParent = positionInParent;
- }
+ private static boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
+ return taskInfo.getConfiguration().windowConfiguration.getDisplayWindowingMode()
+ == WINDOWING_MODE_FREEFORM;
}
+
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 44d22029a5e9..afb64c9eec41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -33,6 +33,7 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.SystemProperties;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -58,6 +59,8 @@ import kotlin.jvm.functions.Function0;
public class PipMotionHelper implements PipAppOpsListener.Callback,
FloatingContentCoordinator.FloatingContent {
+ public static final boolean ENABLE_FLING_TO_DISMISS_PIP =
+ SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_pip", true);
private static final String TAG = "PipMotionHelper";
private static final boolean DEBUG = false;
@@ -704,6 +707,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
loc[1] = animatedPipBounds.top;
}
};
+ mMagnetizedPip.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_PIP);
}
return mMagnetizedPip;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 6c659667a4a7..cff60f5e5b6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -44,6 +44,8 @@ import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
+import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
@@ -903,11 +905,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
TransitionInfo.AnimationOptions options, float cornerRadius) {
- final boolean isTask = change.getTaskInfo() != null;
final boolean isOpen = Transitions.isOpeningType(change.getMode());
final boolean isClose = Transitions.isClosingType(change.getMode());
if (isOpen) {
- if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
+ if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
attachCrossProfileThumbnailAnimation(animations, finishCallback, change,
cornerRadius);
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
@@ -922,8 +923,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
final Rect bounds = change.getEndAbsBounds();
// Show the right drawable depending on the user we're transitioning to.
- final Drawable thumbnailDrawable = change.getTaskInfo().userId == mCurrentUserId
- ? mContext.getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable;
+ final Drawable thumbnailDrawable = change.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL)
+ ? mContext.getDrawable(R.drawable.ic_account_circle)
+ : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL)
+ ? mEnterpriseThumbnailDrawable : null;
+ if (thumbnailDrawable == null) {
+ return;
+ }
final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
thumbnailDrawable, bounds);
if (thumbnail == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index bdcdb63d2cd6..cc4d268a0000 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -34,4 +34,9 @@ interface IShellTransitions {
* Unregisters a remote transition handler.
*/
oneway void unregisterRemote(in RemoteTransition remoteTransition) = 2;
+
+ /**
+ * Retrieves the apply-token used by transactions in Shell
+ */
+ IBinder getShellApplyToken() = 3;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 29d25bc39223..d2e8624171f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -958,6 +958,11 @@ public class Transitions implements RemoteCallable<Transitions> {
transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
+
+ @Override
+ public IBinder getShellApplyToken() {
+ return SurfaceControl.Transaction.getDefaultApplyToken();
+ }
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index dc3deb1a927c..8b13721ef428 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -142,7 +142,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
return;
}
- if (oldDecorationSurface != mDecorationContainerSurface) {
+ if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener(
mContext,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 330c9c95e484..cb74315732ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -20,6 +20,7 @@ package com.android.wm.shell.flicker
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.common.region.Region
@@ -94,25 +95,27 @@ fun FlickerTestParameter.layerKeepVisible(
fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.isInvisible(component)
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
- .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- .isVisible(component)
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
.then()
- .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ .splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
.then()
.isVisible(component, true)
.then()
@@ -122,58 +125,96 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayersEnd {
- val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, endRotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, endRotation)
- }
- )
+ splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsKeepVisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsChanges(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- if (splitLeftTop) {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ if (landscapePosLeft) {
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
.then()
.isInvisible(component)
.then()
- .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ .splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
} else {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
}
}
}
fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
- splitLeftTop: Boolean,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
rotation: Int
): LayersTraceSubject {
return invoke("splitAppLayerBoundsSnapToDivider") {
- val dividerRegion = it.layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- it.visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, rotation)
+ it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation)
+ }
+}
+
+fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
+ rotation: Int
+): LayerTraceEntrySubject {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return invoke {
+ val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(component).coversAtMost(
+ if (displayBounds.width > displayBounds.height) {
+ if (landscapePosLeft) {
+ Region.from(
+ 0,
+ 0,
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ displayBounds.bounds.bottom)
+ } else {
+ Region.from(
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ 0,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
} else {
- getSplitRightBottomRegion(dividerRegion, rotation)
+ if (portraitPosTop) {
+ Region.from(
+ 0,
+ 0,
+ displayBounds.bounds.right,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2)
+ } else {
+ Region.from(
+ 0,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
}
)
}
@@ -185,6 +226,10 @@ fun FlickerTestParameter.appWindowBecomesVisible(
assertWm {
this.isAppWindowInvisible(component)
.then()
+ .notContains(component, isOptional = true)
+ .then()
+ .isAppWindowInvisible(component, isOptional = true)
+ .then()
.isAppWindowVisible(component)
}
}
@@ -208,7 +253,7 @@ fun FlickerTestParameter.appWindowIsVisibleAtEnd(
}
fun FlickerTestParameter.appWindowKeepVisible(
- component: IComponentMatcher
+ component: IComponentMatcher
) {
assertWm {
this.isAppWindowVisible(component)
@@ -316,39 +361,3 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
)
}
}
-
-fun getSplitLeftTopRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
- Region.from(
- 0,
- 0,
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
- displayBounds.bounds.bottom)
- } else {
- Region.from(
- 0,
- 0,
- displayBounds.bounds.right,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2)
- }
-}
-
-fun getSplitRightBottomRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
- Region.from(
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
- 0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
- )
- } else {
- Region.from(
- 0,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index 1950e486f34b..3ad92f87421b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -21,6 +21,7 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -58,26 +59,27 @@ open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
setup {
test {
for (i in 1..3) {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
+ val addBubbleBtn = waitAndGetAddBubbleBtn() ?: error("Add Bubble not found")
+ addBubbleBtn.click()
+ SystemClock.sleep(1000)
}
val showBubble = device.wait(
Until.findObject(
By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)
), FIND_OBJECT_TIMEOUT
- )
- showBubble?.run { showBubble.click() } ?: error("Show bubble not found")
+ ) ?: error("Show bubble not found")
+ showBubble.click()
SystemClock.sleep(1000)
}
}
transitions {
- val bubbles = device.wait(
+ val bubbles: List<UiObject2> = device.wait(
Until.findObjects(
By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)
), FIND_OBJECT_TIMEOUT
) ?: error("No bubbles found")
for (entry in bubbles) {
- entry?.run { entry.click() } ?: error("Bubble not found")
+ entry.click()
SystemClock.sleep(1000)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 240e8711d43e..e7f9d9a9d73d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -105,6 +105,25 @@ class SplitScreenHelper(
.waitForAndVerify()
}
+ fun splitFromOverview(tapl: LauncherInstrumentation) {
+ // Note: The initial split position in landscape is different between tablet and phone.
+ // In landscape, tablet will let the first app split to right side, and phone will
+ // split to left side.
+ if (tapl.isTablet) {
+ tapl.workspace.switchToOverview().overviewActions
+ .clickSplit()
+ .currentTask
+ .open()
+ } else {
+ tapl.workspace.switchToOverview().currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
fun dragFromNotificationToSplit(
instrumentation: Instrumentation,
device: UiDevice,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index f69107eae638..d23881475ad6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -92,12 +92,12 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS
@Presubmit
@Test
fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
- primaryApp, splitLeftTop = true)
+ primaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
- textEditApp, splitLeftTop = false)
+ textEditApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index cd92db74af95..ba40c2740bb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -98,7 +98,7 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 127ac1e7162b..6828589656d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -96,12 +96,12 @@ class DismissSplitScreenByGoHome(
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 0f4d98d69c00..9ac7c230096b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -108,12 +108,12 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes
@Presubmit
@Test
fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
/** {@inheritDoc} */
@Postsubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 9564d975194b..8401c1a910b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -94,12 +94,12 @@ class EnterSplitScreenByDragFromAllApps(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 3b59716180b6..168afda119a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -109,12 +109,12 @@ class EnterSplitScreenByDragFromNotification(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- sendNotificationApp, splitLeftTop = true)
+ sendNotificationApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 3de98723e132..c1fce5f40b57 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -97,12 +97,12 @@ class EnterSplitScreenByDragFromTaskbar(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
new file mode 100644
index 000000000000..8cb5d7c24ced
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen from Overview.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ SplitScreenHelper.splitFromOverview(tapl)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index bdfd9c7de32f..153056188d24 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -94,12 +94,12 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = true)
+ primaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = false)
+ secondaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index da954d97aec2..20544bd2fc2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -98,12 +98,12 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index db89ff52178b..5a8604f2dccc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -97,12 +97,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index c23cdb610671..adea66a49c46 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -99,12 +99,12 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/media/Android.bp b/media/Android.bp
index ec243bf2370a..7118afad975a 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -35,8 +35,8 @@ aidl_interface {
"aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
],
imports: [
- "android.media.audio.common.types-V1",
- "android.media.soundtrigger.types-V1",
+ "android.media.audio.common.types",
+ "android.media.soundtrigger.types",
"media_permission-aidl",
],
}
@@ -52,6 +52,7 @@ aidl_interface {
],
local_include_dir: "aidl",
srcs: [
+ "aidl/android/media/audio/common/AudioAttributes.aidl",
"aidl/android/media/audio/common/AudioChannelLayout.aidl",
"aidl/android/media/audio/common/AudioConfig.aidl",
"aidl/android/media/audio/common/AudioConfigBase.aidl",
@@ -63,6 +64,7 @@ aidl_interface {
"aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl",
"aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
"aidl/android/media/audio/common/AudioEncapsulationType.aidl",
+ "aidl/android/media/audio/common/AudioFlag.aidl",
"aidl/android/media/audio/common/AudioFormatDescription.aidl",
"aidl/android/media/audio/common/AudioFormatType.aidl",
"aidl/android/media/audio/common/AudioGain.aidl",
@@ -122,8 +124,75 @@ aidl_interface {
version: "1",
imports: [],
},
+ // IMPORTANT: Update latest_android_media_audio_common_types every time
+ // you add the latest frozen version to versions_with_info
+ ],
+
+}
+
+// Note: This should always be one version ahead of the last frozen version
+latest_android_media_audio_common_types = "android.media.audio.common.types-V2"
+
+// Modules that depend on android.media.audio.common.types directly can include
+// the following cc_defaults to avoid explicitly managing dependency versions
+// across many scattered files.
+cc_defaults {
+ name: "latest_android_media_audio_common_types_cpp_shared",
+ shared_libs: [
+ latest_android_media_audio_common_types + "-cpp",
+ ],
+}
+
+cc_defaults {
+ name: "latest_android_media_audio_common_types_cpp_export_shared",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_shared",
],
+ export_shared_lib_headers: [
+ latest_android_media_audio_common_types + "-cpp",
+ ],
+}
+
+cc_defaults {
+ name: "latest_android_media_audio_common_types_cpp_static",
+ static_libs: [
+ latest_android_media_audio_common_types + "-cpp",
+ ],
+}
+cc_defaults {
+ name: "latest_android_media_audio_common_types_cpp_export_static",
+ defaults: [
+ "latest_android_media_audio_common_types_cpp_static",
+ ],
+ export_static_lib_headers: [
+ latest_android_media_audio_common_types + "-cpp",
+ ],
+}
+
+cc_defaults {
+ name: "latest_android_media_audio_common_types_ndk_shared",
+ shared_libs: [
+ latest_android_media_audio_common_types + "-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "latest_android_media_audio_common_types_ndk_static",
+ static_libs: [
+ latest_android_media_audio_common_types + "-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "latest_android_media_audio_common_types_cpp_target_shared",
+ target: {
+ android: {
+ shared_libs: [
+ latest_android_media_audio_common_types + "-cpp",
+ ],
+ },
+ },
}
aidl_interface {
diff --git a/media/aidl/android/media/audio/common/AudioAttributes.aidl b/media/aidl/android/media/audio/common/AudioAttributes.aidl
new file mode 100644
index 000000000000..eb29e10ab3a5
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioAttributes.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioContentType;
+import android.media.audio.common.AudioFlag;
+import android.media.audio.common.AudioSource;
+import android.media.audio.common.AudioUsage;
+
+/**
+ * AudioAttributes give information about an audio stream that is more
+ * descriptive than stream type alone.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioAttributes {
+ /**
+ * Classifies the content of the audio signal using categories such as
+ * speech or music
+ */
+ AudioContentType contentType = AudioContentType.UNKNOWN;
+ /**
+ * Classifies the intended use of the audio signal using categories such as
+ * alarm or ringtone
+ */
+ AudioUsage usage = AudioUsage.UNKNOWN;
+ /**
+ * Classifies the audio source using categories such as voice uplink or
+ * remote submix
+ */
+ AudioSource source = AudioSource.DEFAULT;
+ /**
+ * Bitmask describing how playback is to be affected.
+ */
+ int flags = AudioFlag.NONE;
+ /**
+ * Tag is an additional use case qualifier complementing AudioUsage and
+ * AudioContentType. Tags are set by vendor-specific applications and must
+ * be prefixed by "VX_". Vendors must namespace their tag names using the
+ * name of their company to avoid conflicts. The namespace must use at least
+ * three characters, and must go directly after the "VX_" prefix.
+ * For example: "VX_MYCOMPANY_VR".
+ */
+ @utf8InCpp String[] tags;
+} \ No newline at end of file
diff --git a/media/aidl/android/media/audio/common/AudioFlag.aidl b/media/aidl/android/media/audio/common/AudioFlag.aidl
new file mode 100644
index 000000000000..b9d493e620ab
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioFlag.aidl
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+/**
+ * Defines the audio flags that are used in AudioAttributes
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioFlag {
+ NONE = 0x0,
+ /**
+ * Flag defining a behavior where the audibility of the sound will be
+ * ensured by the system. To ensure sound audibility, the system only uses
+ * built-in speakers or wired headphones and specifically excludes wireless
+ * audio devices. Note this flag should only be used for sounds subject to
+ * regulatory behaviors in some countries, such as for camera shutter sound,
+ * and not for routing behaviors.
+ */
+ AUDIBILITY_ENFORCED = 0x1 << 0,
+ /**
+ * Skipping 0x1 << 1. This was previously used for SECURE flag, but because
+ * the security feature was never implemented using this flag, and the flag
+ * was never made public, this value may be used for another flag.
+ */
+ /**
+ * Flag to enable when the stream is associated with SCO usage.
+ * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
+ */
+ SCO = 0x1 << 2,
+ /**
+ * Flag defining a behavior where the system ensures that the playback of
+ * the sound will be compatible with its use as a broadcast for surrounding
+ * people and/or devices. Ensures audibility with no or minimal
+ * post-processing applied.
+ */
+ BEACON = 0x1 << 3,
+ /**
+ * Flag requesting the use of an output stream supporting hardware A/V
+ * synchronization.
+ */
+ HW_AV_SYNC = 0x1 << 4,
+ /**
+ * Flag requesting capture from the source used for hardware hotword
+ * detection. To be used with capture preset MediaRecorder.AudioSource
+ * HOTWORD or MediaRecorder.AudioSource.VOICE_RECOGNITION.
+ */
+ HW_HOTWORD = 0x1 << 5,
+ /**
+ * Flag requesting audible playback even under limited interruptions.
+ */
+ BYPASS_INTERRUPTION_POLICY = 0x1 << 6,
+ /**
+ * Flag requesting audible playback even when the underlying stream is muted
+ */
+ BYPASS_MUTE = 0x1 << 7,
+ /**
+ * Flag requesting a low latency path when creating an AudioTrack.
+ * When using this flag, the sample rate must match the native sample rate
+ * of the device. Effects processing is also unavailable.
+ */
+ LOW_LATENCY = 0x1 << 8,
+ /**
+ * Flag requesting a deep buffer path when creating an AudioTrack.
+ *
+ * A deep buffer path, if available, may consume less power and is
+ * suitable for media playback where latency is not a concern.
+ */
+ DEEP_BUFFER = 0x1 << 9,
+ /**
+ * Flag specifying that the audio shall not be captured by third-party apps
+ * with a MediaProjection.
+ */
+ NO_MEDIA_PROJECTION = 0x1 << 10,
+ /**
+ * Flag indicating force muting haptic channels.
+ */
+ MUTE_HAPTIC = 0x1 << 11,
+ /**
+ * Flag specifying that the audio shall not be captured by any apps, not
+ * even system apps.
+ */
+ NO_SYSTEM_CAPTURE = 0x1 << 12,
+ /**
+ * Flag requesting private audio capture.
+ */
+ CAPTURE_PRIVATE = 0x1 << 13,
+ /**
+ * Flag indicating the audio content has been processed to provide a virtual
+ * multichannel audio experience.
+ */
+ CONTENT_SPATIALIZED = 0x1 << 14,
+ /**
+ * Flag indicating the audio content is never to be spatialized.
+ */
+ NEVER_SPATIALIZE = 0x1 << 15,
+ /**
+ * Flag indicating the audio is part of a call redirection.
+ * Valid for playback and capture.
+ */
+ CALL_REDIRECTION = 0x1 << 16,
+} \ No newline at end of file
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioAttributes.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioAttributes.aidl
new file mode 100644
index 000000000000..6d5e234af08e
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioAttributes.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioAttributes {
+ android.media.audio.common.AudioContentType contentType = android.media.audio.common.AudioContentType.UNKNOWN;
+ android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.UNKNOWN;
+ android.media.audio.common.AudioSource source = android.media.audio.common.AudioSource.DEFAULT;
+ int flags = 0;
+ @utf8InCpp String[] tags;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFlag.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFlag.aidl
new file mode 100644
index 000000000000..3138531d6545
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFlag.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+@Backing(type="int") @VintfStability
+enum AudioFlag {
+ NONE = 0,
+ AUDIBILITY_ENFORCED = 1,
+ SCO = 4,
+ BEACON = 8,
+ HW_AV_SYNC = 16,
+ HW_HOTWORD = 32,
+ BYPASS_INTERRUPTION_POLICY = 64,
+ BYPASS_MUTE = 128,
+ LOW_LATENCY = 256,
+ DEEP_BUFFER = 512,
+ NO_MEDIA_PROJECTION = 1024,
+ MUTE_HAPTIC = 2048,
+ NO_SYSTEM_CAPTURE = 4096,
+ CAPTURE_PRIVATE = 8192,
+ CONTENT_SPATIALIZED = 16384,
+ NEVER_SPATIALIZE = 32768,
+ CALL_REDIRECTION = 65536,
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6922637350d9..2547a963eb31 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2812,9 +2812,7 @@ public class AudioManager {
}
/**
- * @param on set <var>true</var> to route A2DP audio to/from Bluetooth
- * headset; <var>false</var> disable A2DP audio
- * @deprecated Do not use.
+ * @deprecated Use {@link MediaRouter#selectRoute} instead.
*/
@Deprecated public void setBluetoothA2dpOn(boolean on){
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index bb086d6839eb..dc093595b6a4 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -311,7 +311,7 @@ public class SoundPool extends PlayerBase {
int priority, int loop, float rate) {
// FIXME: b/174876164 implement device id for soundpool
baseStart(0);
- return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
}
/**
@@ -512,7 +512,7 @@ public class SoundPool extends PlayerBase {
@NonNull Object/*AudioAttributes*/ attributes, @NonNull String opPackageName);
private native final int _play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
+ int priority, int loop, float rate, int playerIId);
private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index bbb4d1fa76bc..b1c97300d08a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -344,6 +344,16 @@ public class Tuner implements AutoCloseable {
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public Tuner(@NonNull Context context, @Nullable String tvInputSessionId,
@TvInputService.PriorityHintUseCaseType int useCase) {
+ mContext = context;
+ mTunerResourceManager = mContext.getSystemService(TunerResourceManager.class);
+
+ // The Tuner Resource Manager is only started when the device has the tuner feature.
+ if (mTunerResourceManager == null) {
+ throw new IllegalStateException(
+ "Tuner instance is created, but the device doesn't have tuner feature");
+ }
+
+ // This code will start tuner server if the device is running on the lazy tuner HAL.
nativeSetup();
sTunerVersion = nativeGetTunerVersion();
if (sTunerVersion == TunerVersionChecker.TUNER_VERSION_UNKNOWN) {
@@ -353,9 +363,6 @@ public class Tuner implements AutoCloseable {
+ TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
+ TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
}
- mContext = context;
- mTunerResourceManager = (TunerResourceManager)
- context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
if (mHandler == null) {
mHandler = createEventHandler();
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e3e200fcd754..96a3781af3a8 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -160,7 +160,7 @@ cc_library_shared {
cc_library_shared {
name: "libmedia_tv_tuner",
-
+ min_sdk_version: "",
srcs: [
"android_media_tv_Tuner.cpp",
"tuner/DemuxClient.cpp",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 397c70485d5b..244730b76df2 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1238,7 +1238,8 @@ FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
}
/////////////// Tuner ///////////////////////
-sp<TunerClient> JTuner::mTunerClient;
+sp<TunerClient> JTuner::sTunerClient = nullptr;
+std::mutex JTuner::sTunerClientMutex;
JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) {
jclass clazz = env->GetObjectClass(thiz);
@@ -1246,10 +1247,15 @@ JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) {
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
- if (mTunerClient == nullptr) {
- mTunerClient = new TunerClient();
+ {
+ std::scoped_lock<std::mutex> lock(sTunerClientMutex);
+ if (sTunerClient == nullptr) {
+ sTunerClient = new TunerClient();
+ } else {
+ sTunerClient->incStrong(this);
+ }
+ ALOGV("JTuner refs count: %d", sTunerClient->getStrongCount());
}
-
mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
}
@@ -1271,18 +1277,28 @@ JTuner::~JTuner() {
mFeClient = nullptr;
mFeClientCb = nullptr;
mDemuxClient = nullptr;
+ {
+ std::scoped_lock<std::mutex> lock(sTunerClientMutex);
+ int32_t refCnt = sTunerClient->getStrongCount();
+ ALOGV("~JTuner refs count: %d", refCnt);
+ if (refCnt == 1) {
+ sTunerClient = nullptr;
+ } else {
+ sTunerClient->decStrong(this);
+ }
+ }
mClass = nullptr;
mObject = nullptr;
}
jint JTuner::getTunerVersion() {
ALOGV("JTuner::getTunerVersion()");
- return (jint)mTunerClient->getHalTunerVersion();
+ return (jint)sTunerClient->getHalTunerVersion();
}
jobject JTuner::getFrontendIds() {
ALOGV("JTuner::getFrontendIds()");
- vector<int32_t> ids = mTunerClient->getFrontendIds();
+ vector<int32_t> ids = sTunerClient->getFrontendIds();
if (ids.size() == 0) {
ALOGW("Frontend isn't available");
return nullptr;
@@ -1305,7 +1321,7 @@ jobject JTuner::getFrontendIds() {
jobject JTuner::openFrontendByHandle(int feHandle) {
// TODO: Handle reopening frontend with different handle
- sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle);
+ sp<FrontendClient> feClient = sTunerClient->openFrontend(feHandle);
if (feClient == nullptr) {
ALOGE("Failed to open frontend");
return nullptr;
@@ -1511,7 +1527,7 @@ jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jobject JTuner::getFrontendInfo(int id) {
shared_ptr<FrontendInfo> feInfo;
- feInfo = mTunerClient->getFrontendInfo(id);
+ feInfo = sTunerClient->getFrontendInfo(id);
if (feInfo == nullptr) {
return nullptr;
}
@@ -1605,21 +1621,21 @@ Result JTuner::getFrontendHardwareInfo(string &info) {
}
jint JTuner::setMaxNumberOfFrontends(int32_t type, int32_t maxNumber) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
ALOGE("tuner is not initialized");
return (jint)Result::INVALID_STATE;
}
- return (jint)mTunerClient->setMaxNumberOfFrontends(static_cast<FrontendType>(type), maxNumber);
+ return (jint)sTunerClient->setMaxNumberOfFrontends(static_cast<FrontendType>(type), maxNumber);
}
int32_t JTuner::getMaxNumberOfFrontends(int32_t type) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
ALOGE("tuner is not initialized");
return -1;
}
- return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
+ return sTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
}
jint JTuner::removeOutputPid(int32_t pid) {
@@ -1662,13 +1678,13 @@ jobjectArray JTuner::getFrontendStatusReadiness(jintArray types) {
}
jobject JTuner::openLnbByHandle(int handle) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
return nullptr;
}
sp<LnbClient> lnbClient;
sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
- lnbClient = mTunerClient->openLnb(handle);
+ lnbClient = sTunerClient->openLnb(handle);
if (lnbClient == nullptr) {
ALOGD("Failed to open lnb, handle = %d", handle);
return nullptr;
@@ -1692,7 +1708,7 @@ jobject JTuner::openLnbByHandle(int handle) {
}
jobject JTuner::openLnbByName(jstring name) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
return nullptr;
}
@@ -1700,7 +1716,7 @@ jobject JTuner::openLnbByName(jstring name) {
std::string lnbName(env->GetStringUTFChars(name, nullptr));
sp<LnbClient> lnbClient;
sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
- lnbClient = mTunerClient->openLnbByName(lnbName);
+ lnbClient = sTunerClient->openLnbByName(lnbName);
if (lnbClient == nullptr) {
ALOGD("Failed to open lnb by name, name = %s", lnbName.c_str());
return nullptr;
@@ -1770,20 +1786,20 @@ int JTuner::setLnb(sp<LnbClient> lnbClient) {
}
int JTuner::setLna(bool enable) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
return (int)Result::NOT_INITIALIZED;
}
- Result result = mTunerClient->setLna(enable);
+ Result result = sTunerClient->setLna(enable);
return (int)result;
}
Result JTuner::openDemux(int handle) {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
return Result::NOT_INITIALIZED;
}
if (mDemuxClient == nullptr) {
- mDemuxClient = mTunerClient->openDemux(handle);
+ mDemuxClient = sTunerClient->openDemux(handle);
if (mDemuxClient == nullptr) {
ALOGE("Failed to open demux");
return Result::UNKNOWN_ERROR;
@@ -1881,10 +1897,10 @@ int JTuner::unlinkCiCam(int id) {
jobject JTuner::openDescrambler() {
ALOGV("JTuner::openDescrambler");
- if (mTunerClient == nullptr || mDemuxClient == nullptr) {
+ if (sTunerClient == nullptr || mDemuxClient == nullptr) {
return nullptr;
}
- sp<DescramblerClient> descramblerClient = mTunerClient->openDescrambler(0/*unused*/);
+ sp<DescramblerClient> descramblerClient = sTunerClient->openDescrambler(0 /*unused*/);
if (descramblerClient == nullptr) {
ALOGD("Failed to open descrambler");
@@ -1995,12 +2011,12 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
}
jobject JTuner::getDemuxCaps() {
- if (mTunerClient == nullptr) {
+ if (sTunerClient == nullptr) {
return nullptr;
}
shared_ptr<DemuxCapabilities> caps;
- caps = mTunerClient->getDemuxCaps();
+ caps = sTunerClient->getDemuxCaps();
if (caps == nullptr) {
return nullptr;
}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 03e7fa91653d..c74b2df6c178 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -216,7 +216,8 @@ protected:
private:
jclass mClass;
jweak mObject;
- static sp<TunerClient> mTunerClient;
+ static sp<TunerClient> sTunerClient;
+ static std::mutex sTunerClientMutex;
sp<FrontendClient> mFeClient;
sp<FrontendClientCallbackImpl> mFeClientCb;
int mFeId;
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index a2826cb58ccf..9f32a83ad5be 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -115,7 +115,7 @@ bool SoundPool::unload(int32_t soundID)
}
int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
- int32_t priority, int32_t loop, float rate)
+ int32_t priority, int32_t loop, float rate, int32_t playerIId)
{
ALOGV("%s(soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
__func__, soundID, leftVolume, rightVolume, priority, loop, rate);
@@ -136,8 +136,9 @@ int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
}
const int32_t streamID = mStreamManager.queueForPlay(
- sound, soundID, leftVolume, rightVolume, priority, loop, rate);
+ sound, soundID, leftVolume, rightVolume, priority, loop, rate, playerIId);
ALOGV("%s returned %d", __func__, streamID);
+
return streamID;
}
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 6bb971b07e43..efc358ddf3b2 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -39,7 +39,7 @@ public:
int32_t load(int fd, int64_t offset, int64_t length, int32_t priority);
bool unload(int32_t soundID);
int32_t play(int32_t soundID, float leftVolume, float rightVolume, int32_t priority,
- int32_t loop, float rate);
+ int32_t loop, float rate, int32_t playerIId = PLAYER_PIID_INVALID);
void pause(int32_t streamID);
void autoPause();
void resume(int32_t streamID);
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 9ed8770a455c..4194a22bfb67 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -229,7 +229,7 @@ Stream* Stream::getPairStream() const
return mStreamManager->getPairStream(this);
}
-Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
+Stream* Stream::playPairStream(std::vector<std::any>& garbage, int32_t playerIId) {
Stream* pairStream = getPairStream();
LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
{
@@ -260,7 +260,7 @@ Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
const int pairState = pairStream->mState;
pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
- pairStream->mLoop, pairStream->mRate, garbage);
+ pairStream->mLoop, pairStream->mRate, garbage, playerIId);
if (pairStream->mState == IDLE) {
return nullptr; // AudioTrack error
}
@@ -274,12 +274,12 @@ Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
- std::vector<std::any>& garbage)
+ std::vector<std::any>& garbage, int32_t playerIId)
{
ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
- " priority=%d, loop=%d, rate=%f)",
+ " priority=%d, loop=%d, rate=%f, playerIId=%d)",
__func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
- priority, loop, rate);
+ priority, loop, rate, playerIId);
// initialize track
const audio_stream_type_t streamType =
@@ -340,6 +340,10 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
mAudioTrack->setCallerName("soundpool");
+ if (playerIId != PLAYER_PIID_INVALID) {
+ mAudioTrack->setPlayerIId(playerIId);
+ }
+
if (status_t status = mAudioTrack->initCheck();
status != NO_ERROR) {
ALOGE("%s: error %d creating AudioTrack", __func__, status);
@@ -379,6 +383,7 @@ int Stream::getCorrespondingStreamID() {
std::lock_guard lock(mLock);
return static_cast<int>(mAudioTrack ? mStreamID : getPairStream()->mStreamID);
}
+
size_t Stream::StreamCallback::onMoreData(const AudioTrack::Buffer&) {
ALOGW("%s streamID %d Unexpected EVENT_MORE_DATA for static track",
__func__, mStream->getCorrespondingStreamID());
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
index 0054eeca529a..6c9ef2e087f8 100644
--- a/media/jni/soundpool/Stream.h
+++ b/media/jni/soundpool/Stream.h
@@ -93,7 +93,8 @@ public:
// returns the pair stream if successful, nullptr otherwise.
// garbage is used to release tracks and data outside of any lock.
- Stream* playPairStream(std::vector<std::any>& garbage);
+ Stream* playPairStream(std::vector<std::any>& garbage,
+ int32_t playerIId = PLAYER_PIID_INVALID);
// These parameters are explicitly checked in the SoundPool class
// so never deviate from the Java API specified values.
@@ -157,7 +158,7 @@ private:
// garbage is used to release tracks and data outside of any lock.
void play_l(const std::shared_ptr<Sound>& sound, int streamID,
float leftVolume, float rightVolume, int priority, int loop, float rate,
- std::vector<std::any>& garbage) REQUIRES(mLock);
+ std::vector<std::any>& garbage, int playerIId) REQUIRES(mLock);
void stop_l() REQUIRES(mLock);
void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 487a696d8765..acd4badad9b0 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -151,10 +151,13 @@ StreamManager::~StreamManager()
int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
int32_t soundID, float leftVolume, float rightVolume,
- int32_t priority, int32_t loop, float rate)
+ int32_t priority, int32_t loop, float rate, int32_t playerIId)
{
- ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
- __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
+ ALOGV(
+ "%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f,"
+ " playerIId=%d)", __func__, sound.get(), soundID, leftVolume, rightVolume, priority,
+ loop, rate, playerIId);
+
bool launchThread = false;
int32_t streamID = 0;
std::vector<std::any> garbage;
@@ -244,7 +247,7 @@ int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
removeFromQueues_l(newStream);
mProcessingStreams.emplace(newStream);
lock.unlock();
- if (Stream* nextStream = newStream->playPairStream(garbage)) {
+ if (Stream* nextStream = newStream->playPairStream(garbage, playerIId)) {
lock.lock();
ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
addToActiveQueue_l(nextStream);
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index ec65b0c49dc4..adbab4b0f9d9 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -394,7 +394,7 @@ public:
// Returns positive streamID on success, 0 on failure. This is locked.
int32_t queueForPlay(const std::shared_ptr<Sound> &sound,
int32_t soundID, float leftVolume, float rightVolume,
- int32_t priority, int32_t loop, float rate)
+ int32_t priority, int32_t loop, float rate, int32_t playerIId)
NO_THREAD_SAFETY_ANALYSIS; // uses unique_lock
///////////////////////////////////////////////////////////////////////
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 5264772be7c3..25040a942061 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -364,12 +364,19 @@ android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
static jint
android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
- jfloat rate)
+ jfloat rate, jint playerIId)
{
ALOGV("android_media_SoundPool_play\n");
auto soundPool = getSoundPool(env, thiz);
if (soundPool == nullptr) return 0;
- return (jint) soundPool->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
+
+ return (jint) soundPool->play(sampleID,
+ leftVolume,
+ rightVolume,
+ priority,
+ loop,
+ rate,
+ playerIId);
}
static void
@@ -563,7 +570,7 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_SoundPool_unload
},
{ "_play",
- "(IFFIIF)I",
+ "(IFFIIFI)I",
(void *)android_media_SoundPool_play
},
{ "pause",
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 3c8fdfe682af..8515874c022f 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -27,16 +27,13 @@ using ::aidl::android::hardware::tv::tuner::FrontendType;
namespace android {
-shared_ptr<ITunerService> TunerClient::mTunerService;
int32_t TunerClient::mTunerVersion;
/////////////// TunerClient ///////////////////////
TunerClient::TunerClient() {
- if (mTunerService == nullptr) {
- ::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
- mTunerService = ITunerService::fromBinder(binder);
- }
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService("media.tuner"));
+ mTunerService = ITunerService::fromBinder(binder);
if (mTunerService == nullptr) {
ALOGE("Failed to get tuner service");
} else {
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index a9f37e6df3aa..5410c1b185f5 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -150,10 +150,11 @@ public:
private:
/**
- * An AIDL Tuner Service Singleton assigned at the first time the Tuner Client
- * connects with the Tuner Service. Default null when the service does not exist.
+ * An AIDL Tuner Service assigned at the first time the Tuner Client connects with
+ * the Tuner Service. null when the service does not exist. The tuner client in JNI
+ * will be singleton, so this Tuner Service client will be singleton too.
*/
- static shared_ptr<ITunerService> mTunerService;
+ shared_ptr<ITunerService> mTunerService;
// An integer that carries the Tuner version. The high 16 bits are the major version number
// while the low 16 bits are the minor version. Default value is unknown version 0.
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 35d13230a3b7..2aa26e321a91 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -20,6 +20,11 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="?android:attr/colorBackground"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingTop="@dimen/settingslib_switchbar_margin"
+ android:paddingBottom="@dimen/settingslib_switchbar_margin"
android:orientation="vertical">
<LinearLayout
@@ -27,7 +32,6 @@
android:minHeight="@dimen/settingslib_min_switch_bar_height"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_margin="@dimen/settingslib_switchbar_margin"
android:paddingStart="@dimen/settingslib_switchbar_padding_left"
android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index b5be7cd45688..22299861e0e3 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -22,7 +22,7 @@
android:label="@string/app_label"
android:supportsRtl="true">
<activity
- android:name="com.android.settingslib.spa.gallery.MainActivity"
+ android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
index 51d3714ce5ee..12be0702b3ee 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
@@ -36,24 +36,28 @@ open class SpaActivity(
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
+
setContent {
- MainContent()
+ SettingsTheme {
+ MainContent()
+ }
}
}
@Composable
private fun MainContent() {
- SettingsTheme {
- val navController = rememberNavController()
- CompositionLocalProvider(navController.localNavController()) {
- NavHost(navController, settingsPageRepository.startDestination) {
- for (page in settingsPageRepository.allPages) {
- composable(
- route = page.route,
- arguments = page.arguments,
- ) { navBackStackEntry ->
- page.Page(navBackStackEntry.arguments)
- }
+ val startDestination =
+ intent?.getStringExtra(KEY_START_DESTINATION) ?: settingsPageRepository.startDestination
+
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, startDestination) {
+ for (page in settingsPageRepository.allPages) {
+ composable(
+ route = page.route,
+ arguments = page.arguments,
+ ) { navBackStackEntry ->
+ page.Page(navBackStackEntry.arguments)
}
}
}
@@ -62,4 +66,8 @@ open class SpaActivity(
private val SettingsPageProvider.route: String
get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
+
+ companion object {
+ const val KEY_START_DESTINATION = "spa:SpaActivity:startDestination"
+ }
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 0a41a1a95936..c9602543b364 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -18,6 +18,7 @@ package com.android.settingslib.spa.widget.scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
+import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
@@ -44,3 +45,15 @@ private fun BackAction(contentDescription: String, onClick: () -> Unit) {
)
}
}
+
+@Composable
+fun MoreOptionsAction(onClick: () -> Unit) {
+ IconButton(onClick) {
+ Icon(
+ imageVector = Icons.Outlined.MoreVert,
+ contentDescription = stringResource(
+ id = androidx.appcompat.R.string.abc_action_menu_overflow_description,
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
index f608e1003163..0c84eac45cb7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
@@ -18,6 +18,7 @@ package com.android.settingslib.spa.widget.scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotSelected
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createComposeRule
@@ -43,7 +44,7 @@ class SettingsPagerKtTest {
composeTestRule.onNodeWithText("Personal").assertIsSelected()
composeTestRule.onNodeWithText("Page 0").assertIsDisplayed()
composeTestRule.onNodeWithText("Work").assertIsNotSelected()
- composeTestRule.onNodeWithText("Page 1").assertDoesNotExist()
+ composeTestRule.onNodeWithText("Page 1").assertIsNotDisplayed()
}
@Test
@@ -55,7 +56,7 @@ class SettingsPagerKtTest {
composeTestRule.onNodeWithText("Work").performClick()
composeTestRule.onNodeWithText("Personal").assertIsNotSelected()
- composeTestRule.onNodeWithText("Page 0").assertDoesNotExist()
+ composeTestRule.onNodeWithText("Page 0").assertIsNotDisplayed()
composeTestRule.onNodeWithText("Work").assertIsSelected()
composeTestRule.onNodeWithText("Page 1").assertIsDisplayed()
}
diff --git a/packages/SettingsLib/SpaPrivileged/res/values/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
index 8f8dd2b01ecd..b2302a58229c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
@@ -17,4 +17,12 @@
<resources>
<!-- [CHAR LIMIT=25] Text shown when there are no applications to display. -->
<string name="no_applications">No apps.</string>
+ <!-- [CHAR LIMIT=NONE] Menu for manage apps to control whether system processes are shown -->
+ <string name="menu_show_system">Show system</string>
+ <!-- [CHAR LIMIT=NONE] Menu for manage apps to control whether system processes are hidden -->
+ <string name="menu_hide_system">Hide system</string>
+ <!-- Preference summary text for an app when it is allowed for a permission. [CHAR LIMIT=45] -->
+ <string name="app_permission_summary_allowed">Allowed</string>
+ <!-- Preference summary text for an app when it is disallowed for a permission. [CHAR LIMIT=45] -->
+ <string name="app_permission_summary_not_allowed">Not allowed</string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
index 2fa869c529d4..00eb60b6ec7a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
@@ -2,6 +2,8 @@ package com.android.settingslib.spaprivileged.model.app
import android.content.pm.ApplicationInfo
import android.icu.text.CollationKey
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
import kotlinx.coroutines.flow.Flow
data class AppEntry<T : AppRecord>(
@@ -22,5 +24,6 @@ interface AppListModel<T : AppRecord> {
{ it.record.app.uid },
)
- fun getSummary(option: Int, record: T): Flow<String>?
+ @Composable
+ fun getSummary(option: Int, record: T): State<String>?
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index 58d0f8d398f2..99deb707a351 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -43,19 +43,29 @@ fun AppInfo(packageName: String, userId: Int) {
Column(
modifier = Modifier
.fillMaxWidth()
- .padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally) {
+ .padding(
+ horizontal = SettingsDimension.itemPaddingStart,
+ vertical = SettingsDimension.itemPaddingVertical,
+ ),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
val packageInfo = remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }
- Box(modifier = Modifier.padding(8.dp)) {
+ Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
AppIcon(app = packageInfo.applicationInfo, size = SettingsDimension.appIconInfoSize)
}
AppLabel(packageInfo.applicationInfo)
- Spacer(modifier = Modifier.height(4.dp))
- SettingsBody(packageInfo.versionName)
+ AppVersion(packageInfo.versionName)
}
}
@Composable
+private fun AppVersion(versionName: String?) {
+ if (versionName == null) return
+ Spacer(modifier = Modifier.height(4.dp))
+ SettingsBody(versionName)
+}
+
+@Composable
fun AppIcon(app: ApplicationInfo, size: Dp) {
val appRepository = rememberAppRepository()
Image(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
index 06d7547be309..9b45318ffd82 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
@@ -16,15 +16,8 @@
package com.android.settingslib.spaprivileged.template.app
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Footer
@Composable
@@ -35,15 +28,7 @@ fun AppInfoPage(
footerText: String,
content: @Composable () -> Unit,
) {
- // TODO: Replace with SettingsScaffold
- Column(Modifier.verticalScroll(rememberScrollState())) {
- Text(
- text = title,
- modifier = Modifier.padding(SettingsDimension.itemPadding),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.headlineMedium,
- )
-
+ RegularScaffold(title = title) {
AppInfo(packageName, userId)
content()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index c60976ddea4d..315dc5d6ff70 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -42,7 +42,7 @@ import kotlinx.coroutines.Dispatchers
private const val TAG = "AppList"
@Composable
-fun <T : AppRecord> AppList(
+internal fun <T : AppRecord> AppList(
userInfo: UserInfo,
listModel: AppListModel<T>,
showSystem: State<Boolean>,
@@ -73,7 +73,7 @@ private fun <T : AppRecord> AppListWidget(
) {
items(count = list.size, key = { option to list[it].record.app.packageName }) {
val appEntry = list[it]
- val summary = getSummary(listModel, option, appEntry.record)
+ val summary = listModel.getSummary(option, appEntry.record) ?: "".toState()
val itemModel = remember(appEntry) {
AppListItemModel(appEntry.record, appEntry.label, summary)
}
@@ -100,12 +100,3 @@ private fun <T : AppRecord> loadAppEntries(
return viewModel.appListDataFlow.collectAsState(null, Dispatchers.Default)
}
-
-@Composable
-private fun <T : AppRecord> getSummary(
- listModel: AppListModel<T>,
- option: Int,
- record: T,
-): State<String> = remember(option) { listModel.getSummary(option, record) }
- ?.collectAsState(stringResource(R.string.summary_placeholder), Dispatchers.Default)
- ?: "".toState()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
new file mode 100644
index 000000000000..dc30e796f64f
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
+import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
+
+@Composable
+fun <T : AppRecord> AppListPage(
+ title: String,
+ listModel: AppListModel<T>,
+ appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
+) {
+ val showSystem = rememberSaveable { mutableStateOf(false) }
+ // TODO: Use SearchScaffold here.
+ SettingsScaffold(
+ title = title,
+ actions = {
+ ShowSystemAction(showSystem.value) { showSystem.value = it }
+ },
+ ) { paddingValues ->
+ Spacer(Modifier.padding(paddingValues))
+ WorkProfilePager { userInfo ->
+ // TODO: Add a Spinner here.
+ AppList(
+ userInfo = userInfo,
+ listModel = listModel,
+ showSystem = showSystem,
+ option = stateOf(0),
+ searchQuery = stateOf(""),
+ appItem = appItem,
+ )
+ }
+ }
+}
+
+@Composable
+private fun ShowSystemAction(showSystem: Boolean, setShowSystem: (showSystem: Boolean) -> Unit) {
+ var expanded by remember { mutableStateOf(false) }
+ MoreOptionsAction { expanded = true }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ ) {
+ val menuText = if (showSystem) R.string.menu_hide_system else R.string.menu_show_system
+ DropdownMenuItem(
+ text = { Text(stringResource(menuText)) },
+ onClick = {
+ expanded = false
+ setShowSystem(!showSystem)
+ },
+ )
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 430ac5409711..298ecd5106ea 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spaprivileged.template.app
import android.content.Context
+import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -28,21 +29,24 @@ import androidx.compose.ui.res.stringResource
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.PackageManagers
+import com.android.settingslib.spaprivileged.model.app.toRoute
import kotlinx.coroutines.Dispatchers
+private const val NAME = "TogglePermissionAppInfoPage"
private const val PERMISSION = "permission"
private const val PACKAGE_NAME = "packageName"
private const val USER_ID = "userId"
-open class TogglePermissionAppInfoPageProvider(
+internal class TogglePermissionAppInfoPageProvider(
private val factory: TogglePermissionAppListModelFactory,
) : SettingsPageProvider {
- override val name = "TogglePermissionAppInfoPage"
+ override val name = NAME
override val arguments = listOf(
navArgument(PERMISSION) { type = NavType.StringType },
@@ -60,8 +64,11 @@ open class TogglePermissionAppInfoPageProvider(
TogglePermissionAppInfoPage(listModel, packageName, userId)
}
- fun getRoute(permissionType: String, packageName: String, userId: Int): String =
- "$name/$permissionType/$packageName/$userId"
+ companion object {
+ @Composable
+ internal fun navigator(permissionType: String, app: ApplicationInfo) =
+ navigator(route = "$NAME/$permissionType/${app.toRoute()}")
+ }
}
@Composable
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
index 3782f7cd301e..70ff9a478af5 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
@@ -20,14 +20,25 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.ui.res.stringResource
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberContext
+import com.android.settingslib.spa.framework.util.asyncMapItem
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
+import kotlinx.coroutines.flow.Flow
interface TogglePermissionAppListModel<T : AppRecord> {
val pageTitleResId: Int
val switchTitleResId: Int
val footerResId: Int
+ fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>): Flow<List<T>> =
+ appListFlow.asyncMapItem(::transformItem)
+
fun transformItem(app: ApplicationInfo): T
+ fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<T>>): Flow<List<T>>
@Composable
fun isAllowed(record: T): State<Boolean?>
@@ -41,4 +52,24 @@ interface TogglePermissionAppListModelFactory {
permission: String,
context: Context,
): TogglePermissionAppListModel<out AppRecord>
+
+ fun createPageProviders(): List<SettingsPageProvider> = listOf(
+ TogglePermissionAppListPageProvider(this),
+ TogglePermissionAppInfoPageProvider(this),
+ )
+
+ @Composable
+ fun EntryItem(permissionType: String) {
+ val listModel = rememberModel(permissionType)
+ Preference(
+ object : PreferenceModel {
+ override val title = stringResource(listModel.pageTitleResId)
+ override val onClick = TogglePermissionAppListPageProvider.navigator(permissionType)
+ }
+ )
+ }
}
+
+@Composable
+internal fun TogglePermissionAppListModelFactory.rememberModel(permission: String) =
+ rememberContext { context -> createModel(permission, context) }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
new file mode 100644
index 000000000000..107bf942a2d6
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import kotlinx.coroutines.flow.Flow
+
+private const val NAME = "TogglePermissionAppList"
+private const val PERMISSION = "permission"
+
+internal class TogglePermissionAppListPageProvider(
+ private val factory: TogglePermissionAppListModelFactory,
+) : SettingsPageProvider {
+ override val name = NAME
+
+ override val arguments = listOf(
+ navArgument(PERMISSION) { type = NavType.StringType },
+ )
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ checkNotNull(arguments)
+ val permissionType = checkNotNull(arguments.getString(PERMISSION))
+ TogglePermissionAppList(permissionType)
+ }
+
+ @Composable
+ private fun TogglePermissionAppList(permissionType: String) {
+ val listModel = factory.rememberModel(permissionType)
+ val context = LocalContext.current
+ val internalListModel = remember {
+ TogglePermissionInternalAppListModel(context, listModel)
+ }
+ AppListPage(
+ title = stringResource(listModel.pageTitleResId),
+ listModel = internalListModel,
+ ) { itemModel ->
+ AppListItem(
+ itemModel = itemModel,
+ onClick = TogglePermissionAppInfoPageProvider.navigator(
+ permissionType = permissionType,
+ app = itemModel.record.app,
+ ),
+ )
+ }
+ }
+
+ companion object {
+ @Composable
+ internal fun navigator(permissionType: String) = navigator(route = "$NAME/$permissionType")
+ }
+}
+
+private class TogglePermissionInternalAppListModel<T : AppRecord>(
+ private val context: Context,
+ private val listModel: TogglePermissionAppListModel<T>,
+) : AppListModel<T> {
+ override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+ listModel.transform(userIdFlow, appListFlow)
+
+ override fun filter(userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<T>>) =
+ listModel.filter(userIdFlow, recordListFlow)
+
+ @Composable
+ override fun getSummary(option: Int, record: T): State<String> {
+ val allowed = listModel.isAllowed(record)
+ return remember {
+ derivedStateOf {
+ when (allowed.value) {
+ true -> context.getString(R.string.app_permission_summary_allowed)
+ false -> context.getString(R.string.app_permission_summary_not_allowed)
+ else -> ""
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index aab0d3a08154..06d7bb49c809 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1601,21 +1601,6 @@
<!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_calling">No calling.</string>
- <!-- Screensaver overlay which displays the time. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_time">Time</string>
- <!-- Screensaver overlay which displays the date. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_date">Date</string>
- <!-- Screensaver overlay which displays the weather. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_weather">Weather</string>
- <!-- Screensaver overlay which displays air quality. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_aqi">Air Quality</string>
- <!-- Screensaver overlay which displays cast info. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_cast_info">Cast Info</string>
- <!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_home_controls">Home Controls</string>
- <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_smartspace">Smartspace</string>
-
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 22586171e5cf..1606540da3fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -17,7 +17,6 @@
package com.android.settingslib.dream;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,17 +31,14 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.settingslib.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -64,18 +60,21 @@ public class DreamBackend {
public String toString() {
StringBuilder sb = new StringBuilder(DreamInfo.class.getSimpleName());
sb.append('[').append(caption);
- if (isActive)
+ if (isActive) {
sb.append(",active");
+ }
sb.append(',').append(componentName);
- if (settingsComponentName != null)
+ if (settingsComponentName != null) {
sb.append("settings=").append(settingsComponentName);
+ }
return sb.append(']').toString();
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
- public @interface WhenToDream {}
+ public @interface WhenToDream {
+ }
public static final int WHILE_CHARGING = 0;
public static final int WHILE_DOCKED = 1;
@@ -96,7 +95,8 @@ public class DreamBackend {
COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ComplicationType {}
+ public @interface ComplicationType {
+ }
public static final int COMPLICATION_TYPE_TIME = 1;
public static final int COMPLICATION_TYPE_DATE = 2;
@@ -114,8 +114,6 @@ public class DreamBackend {
private final boolean mDreamsActivatedOnDockByDefault;
private final Set<ComponentName> mDisabledDreams;
private final Set<Integer> mSupportedComplications;
- private final Set<Integer> mDefaultEnabledComplications;
-
private static DreamBackend sInstance;
public static DreamBackend getInstance(Context context) {
@@ -147,13 +145,6 @@ public class DreamBackend {
com.android.internal.R.array.config_supportedDreamComplications))
.boxed()
.collect(Collectors.toSet());
-
- mDefaultEnabledComplications = Arrays.stream(resources.getIntArray(
- com.android.internal.R.array.config_dreamComplicationsEnabledByDefault))
- .boxed()
- // A complication can only be enabled by default if it is also supported.
- .filter(mSupportedComplications::contains)
- .collect(Collectors.toSet());
}
public List<DreamInfo> getDreamInfos() {
@@ -251,11 +242,12 @@ public class DreamBackend {
return null;
}
- public @WhenToDream int getWhenToDreamSetting() {
+ @WhenToDream
+ public int getWhenToDreamSetting() {
return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
: isActivatedOnDock() ? WHILE_DOCKED
- : isActivatedOnSleep() ? WHILE_CHARGING
- : NEVER;
+ : isActivatedOnSleep() ? WHILE_CHARGING
+ : NEVER;
}
public void setWhenToDream(@WhenToDream int whenToDream) {
@@ -283,98 +275,29 @@ public class DreamBackend {
}
}
- /** Returns whether a particular complication is enabled */
- public boolean isComplicationEnabled(@ComplicationType int complication) {
- return getEnabledComplications().contains(complication);
- }
-
/** Gets all complications which have been enabled by the user. */
public Set<Integer> getEnabledComplications() {
- final String enabledComplications = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS);
-
- if (enabledComplications == null) {
- return mDefaultEnabledComplications;
- }
-
- return parseFromString(enabledComplications);
+ return getComplicationsEnabled() ? mSupportedComplications : Collections.emptySet();
}
- /** Gets all dream complications which are supported on this device. **/
- public Set<Integer> getSupportedComplications() {
- return mSupportedComplications;
- }
-
- /**
- * Enables or disables a particular dream complication.
- *
- * @param complicationType The dream complication to be enabled/disabled.
- * @param value If true, the complication is enabled. Otherwise it is disabled.
- */
- public void setComplicationEnabled(@ComplicationType int complicationType, boolean value) {
- if (!mSupportedComplications.contains(complicationType)) return;
-
- Set<Integer> enabledComplications = getEnabledComplications();
- if (value) {
- enabledComplications.add(complicationType);
- } else {
- enabledComplications.remove(complicationType);
- }
-
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
- convertToString(enabledComplications));
+ /** Sets complication enabled state. */
+ public void setComplicationsEnabled(boolean enabled) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, enabled ? 1 : 0);
}
/**
- * Gets the title of a particular complication type to be displayed to the user. If there
- * is no title, null is returned.
+ * Gets whether complications are enabled on this device
*/
- @Nullable
- public CharSequence getComplicationTitle(@ComplicationType int complicationType) {
- int res = 0;
- switch (complicationType) {
- case COMPLICATION_TYPE_TIME:
- res = R.string.dream_complication_title_time;
- break;
- case COMPLICATION_TYPE_DATE:
- res = R.string.dream_complication_title_date;
- break;
- case COMPLICATION_TYPE_WEATHER:
- res = R.string.dream_complication_title_weather;
- break;
- case COMPLICATION_TYPE_AIR_QUALITY:
- res = R.string.dream_complication_title_aqi;
- break;
- case COMPLICATION_TYPE_CAST_INFO:
- res = R.string.dream_complication_title_cast_info;
- break;
- case COMPLICATION_TYPE_HOME_CONTROLS:
- res = R.string.dream_complication_title_home_controls;
- break;
- case COMPLICATION_TYPE_SMARTSPACE:
- res = R.string.dream_complication_title_smartspace;
- break;
- default:
- return null;
- }
- return mContext.getString(res);
- }
-
- private static String convertToString(Set<Integer> set) {
- return set.stream()
- .map(String::valueOf)
- .collect(Collectors.joining(","));
+ public boolean getComplicationsEnabled() {
+ return Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, 1) == 1;
}
- private static Set<Integer> parseFromString(String string) {
- if (TextUtils.isEmpty(string)) {
- return new HashSet<>();
- }
- return Arrays.stream(string.split(","))
- .map(Integer::parseInt)
- .collect(Collectors.toSet());
+ /** Gets all dream complications which are supported on this device. **/
+ public Set<Integer> getSupportedComplications() {
+ return mSupportedComplications;
}
public boolean isEnabled() {
@@ -416,10 +339,11 @@ public class DreamBackend {
public void setActiveDream(ComponentName dream) {
logd("setActiveDream(%s)", dream);
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return;
+ }
try {
- ComponentName[] dreams = { dream };
+ ComponentName[] dreams = {dream};
mDreamManager.setDreamComponents(dream == null ? null : dreams);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set active dream to " + dream, e);
@@ -427,8 +351,9 @@ public class DreamBackend {
}
public ComponentName getActiveDream() {
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return null;
+ }
try {
ComponentName[] dreams = mDreamManager.getDreamComponents();
return dreams != null && dreams.length > 0 ? dreams[0] : null;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 86f7850cf1f2..52b9227fb373 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -34,29 +35,35 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSettings;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettings.ShadowSecure.class})
public final class DreamBackendTest {
private static final int[] SUPPORTED_DREAM_COMPLICATIONS = {1, 2, 3};
- private static final int[] DEFAULT_DREAM_COMPLICATIONS = {1, 3, 4};
+ private static final List<Integer> SUPPORTED_DREAM_COMPLICATIONS_LIST = Arrays.stream(
+ SUPPORTED_DREAM_COMPLICATIONS).boxed().collect(
+ Collectors.toList());
@Mock
private Context mContext;
+ @Mock
+ private ContentResolver mMockResolver;
private DreamBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
final Resources res = mock(Resources.class);
when(mContext.getResources()).thenReturn(res);
when(res.getIntArray(
com.android.internal.R.array.config_supportedDreamComplications)).thenReturn(
SUPPORTED_DREAM_COMPLICATIONS);
- when(res.getIntArray(
- com.android.internal.R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
- DEFAULT_DREAM_COMPLICATIONS);
when(res.getStringArray(
com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
new String[]{});
@@ -69,31 +76,25 @@ public final class DreamBackendTest {
}
@Test
- public void testSupportedComplications() {
- assertThat(mBackend.getSupportedComplications()).containsExactly(1, 2, 3);
- }
-
- @Test
- public void testGetEnabledDreamComplications_default() {
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
- }
-
- @Test
- public void testEnableComplication() {
- mBackend.setComplicationEnabled(/* complicationType= */ 2, true);
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 2, 3);
+ public void testComplicationsEnabledByDefault() {
+ assertThat(mBackend.getComplicationsEnabled()).isTrue();
+ assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
+ SUPPORTED_DREAM_COMPLICATIONS_LIST);
}
@Test
- public void testEnableComplication_notSupported() {
- mBackend.setComplicationEnabled(/* complicationType= */ 5, true);
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+ public void testEnableComplicationExplicitly() {
+ mBackend.setComplicationsEnabled(true);
+ assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
+ SUPPORTED_DREAM_COMPLICATIONS_LIST);
+ assertThat(mBackend.getComplicationsEnabled()).isTrue();
}
@Test
- public void testDisableComplication() {
- mBackend.setComplicationEnabled(/* complicationType= */ 1, false);
- assertThat(mBackend.getEnabledComplications()).containsExactly(3);
+ public void testDisableComplications() {
+ mBackend.setComplicationsEnabled(false);
+ assertThat(mBackend.getEnabledComplications()).isEmpty();
+ assertThat(mBackend.getComplicationsEnabled()).isFalse();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
index d86bd014988e..24037caf4e6c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
@@ -16,6 +16,8 @@
package com.android.settingslib.widget;
+import static android.graphics.text.LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
@@ -97,4 +99,14 @@ public class MainSwitchBarTest {
assertThat(mBar.getVisibility()).isEqualTo(View.GONE);
}
+
+ @Test
+ public void setTitle_shouldSetCorrectLineBreakStyle() {
+ final String title = "title";
+
+ mBar.setTitle(title);
+ final TextView textView = ((TextView) mBar.findViewById(R.id.switch_text));
+
+ assertThat(textView.getLineBreakWordStyle()).isEqualTo(LINE_BREAK_WORD_STYLE_PHRASE);
+ }
}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 26feaf979b20..234ef241f538 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -52,17 +52,6 @@
]
},
{
- "name": "SystemUIGoogleScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
// Permission indicators
"name": "CtsPermission4TestCases",
"options": [
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_success_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_success_lottie.json
new file mode 100644
index 000000000000..c5ed827de0d1
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_success_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":80,"h":80,"nm":"RearFPS_error_to_success","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28,47,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-10.556,-9.889],[7.444,6.555],[34.597,-20.486]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":10,"op":910,"st":10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.95,40,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.721,-7.982],[1.721,-7.982],[1.721,7.5],[-1.721,7.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.002,32.488],"ix":2},"a":{"a":0,"k":[0.002,7.488],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.659,0.6],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.6,0.92],"y":[1,1.096]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[100,110]},{"t":10,"s":[100,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top!","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.681,-1.25],[1.681,-1.25],[1.681,2.213],[-1.681,2.213]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30,38.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.6,0.6],"y":[1,1]},"o":{"x":[0.853,0.853],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.92,0.92],"y":[1.06,1.06]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[110,110]},{"t":10,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom!","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":86,"st":-30,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[4]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":10,"op":21,"st":10,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[4]},{"t":10,"s":[0]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":10,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_success_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_success_lottie.json
new file mode 100644
index 000000000000..3eb95ef1a718
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_success_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":80,"h":80,"nm":"RearFPS_fingerprint_to_success","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28,47,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-10.556,-9.889],[7.444,6.555],[34.597,-20.486]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":10,"op":910,"st":10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[4]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":10,"op":21,"st":10,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.091,40,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-0.386],[4.642,1.931],[2.07,2.703],[-0.001,2.886],[-2.621,2.591],[-4.909,1.813],[-8.182,-0.386]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":20,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
index 0146795f4988..dd2e55d4e7d7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
@@ -35,6 +35,7 @@ open class RegionSamplingInstance(
) {
private var isDark = RegionDarkness.DEFAULT
private var samplingBounds = Rect()
+ private val tmpScreenLocation = IntArray(2)
@VisibleForTesting var regionSampler: RegionSamplingHelper? = null
/**
@@ -99,10 +100,21 @@ open class RegionSamplingInstance(
isDark = convertToClockDarkness(isRegionDark)
updateFun.updateColors()
}
-
+ /**
+ * The method getLocationOnScreen is used to obtain the view coordinates
+ * relative to its left and top edges on the device screen.
+ * Directly accessing the X and Y coordinates of the view returns the
+ * location relative to its parent view instead.
+ */
override fun getSampledRegion(sampledView: View): Rect {
- samplingBounds = Rect(sampledView.left, sampledView.top,
- sampledView.right, sampledView.bottom)
+ val screenLocation = tmpScreenLocation
+ sampledView.getLocationOnScreen(screenLocation)
+ val left = screenLocation[0]
+ val top = screenLocation[1]
+ samplingBounds.left = left
+ samplingBounds.top = top
+ samplingBounds.right = left + sampledView.width
+ samplingBounds.bottom = top + sampledView.height
return samplingBounds
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index d718a240bbfd..6c452bd97ff6 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -45,7 +45,7 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
- fun v(@CompileTimeConstant msg: String) = log(msg, ERROR)
+ fun v(@CompileTimeConstant msg: String) = log(msg, VERBOSE)
fun w(@CompileTimeConstant msg: String) = log(msg, WARNING)
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt
new file mode 100644
index 000000000000..f54bf026a686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardViewMediatorLogger.kt
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard.logging
+
+import android.os.RemoteException
+import android.view.WindowManagerPolicyConstants
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.LogLevel.WTF
+import com.android.systemui.log.dagger.KeyguardViewMediatorLog
+import javax.inject.Inject
+
+private const val TAG = "KeyguardViewMediatorLog"
+
+@SysUISingleton
+class KeyguardViewMediatorLogger @Inject constructor(
+ @KeyguardViewMediatorLog private val logBuffer: LogBuffer,
+) {
+
+ fun logFailedLoadLockSound(soundPath: String) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { str1 = soundPath },
+ { "failed to load lock sound from $str1" }
+ )
+ }
+
+ fun logFailedLoadUnlockSound(soundPath: String) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { str1 = soundPath },
+ { "failed to load unlock sound from $str1" }
+ )
+ }
+
+ fun logFailedLoadTrustedSound(soundPath: String) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { str1 = soundPath },
+ { "failed to load trusted sound from $str1" }
+ )
+ }
+
+ fun logOnSystemReady() {
+ logBuffer.log(TAG, DEBUG, "onSystemReady")
+ }
+
+ fun logOnStartedGoingToSleep(offReason: Int) {
+ val offReasonString = WindowManagerPolicyConstants.offReasonToString(offReason)
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = offReasonString },
+ { "onStartedGoingToSleep($str1)" }
+ )
+ }
+
+ fun logPendingExitSecureCallbackCancelled() {
+ logBuffer.log(TAG, DEBUG, "pending exit secure callback cancelled")
+ }
+
+ fun logFailedOnKeyguardExitResultFalse(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onKeyguardExitResult(false)",
+ remoteException
+ )
+ }
+
+ fun logOnFinishedGoingToSleep(offReason: Int) {
+ val offReasonString = WindowManagerPolicyConstants.offReasonToString(offReason)
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = offReasonString },
+ { "onFinishedGoingToSleep($str1)" }
+ )
+ }
+
+ fun logPinLockRequestedStartingKeyguard() {
+ logBuffer.log(TAG, INFO, "PIN lock requested, starting keyguard")
+ }
+
+ fun logUserSwitching(userId: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "onUserSwitching $int1" }
+ )
+ }
+
+ fun logOnUserSwitchComplete(userId: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "onUserSwitchComplete $int1" }
+ )
+ }
+
+ fun logOnSimStateChanged(subId: Int, slotId: Int, simState: String) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = subId
+ int2 = slotId
+ str1 = simState
+ },
+ { "onSimStateChanged(subId=$int1, slotId=$int2, state=$str1)" }
+ )
+ }
+
+ fun logFailedToCallOnSimSecureStateChanged(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onSimSecureStateChanged",
+ remoteException
+ )
+ }
+
+ fun logIccAbsentIsNotShowing() {
+ logBuffer.log(TAG, DEBUG, "ICC_ABSENT isn't showing, we need to show the " +
+ "keyguard since the device isn't provisioned yet.")
+ }
+
+ fun logSimMovedToAbsent() {
+ logBuffer.log(TAG, DEBUG, "SIM moved to ABSENT when the " +
+ "previous state was locked. Reset the state.")
+ }
+
+ fun logIntentValueIccLocked() {
+ logBuffer.log(TAG, DEBUG, "INTENT_VALUE_ICC_LOCKED and keyguard isn't " +
+ "showing; need to show keyguard so user can enter sim pin")
+ }
+
+ fun logPermDisabledKeyguardNotShowing() {
+ logBuffer.log(TAG, DEBUG, "PERM_DISABLED and keyguard isn't showing.")
+ }
+
+ fun logPermDisabledResetStateLocked() {
+ logBuffer.log(TAG, DEBUG, "PERM_DISABLED, resetStateLocked to show permanently " +
+ "disabled message in lockscreen.")
+ }
+
+ fun logReadyResetState(showing: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = showing },
+ { "READY, reset state? $bool1"}
+ )
+ }
+
+ fun logSimMovedToReady() {
+ logBuffer.log(TAG, DEBUG, "SIM moved to READY when the previously was locked. " +
+ "Reset the state.")
+ }
+
+ fun logUnspecifiedSimState(simState: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = simState },
+ { "Unspecific state: $int1" }
+ )
+ }
+
+ fun logOccludeLaunchAnimationCancelled(occluded: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = occluded },
+ { "Occlude launch animation cancelled. Occluded state is now: $bool1"}
+ )
+ }
+
+ fun logActivityLaunchAnimatorLaunchContainerChanged() {
+ logBuffer.log(TAG, WTF, "Someone tried to change the launch container for the " +
+ "ActivityLaunchAnimator, which should never happen.")
+ }
+
+ fun logVerifyUnlock() {
+ logBuffer.log(TAG, DEBUG, "verifyUnlock")
+ }
+
+ fun logIgnoreUnlockDeviceNotProvisioned() {
+ logBuffer.log(TAG, DEBUG, "ignoring because device isn't provisioned")
+ }
+
+ fun logFailedToCallOnKeyguardExitResultFalse(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onKeyguardExitResult(false)",
+ remoteException
+ )
+ }
+
+ fun logVerifyUnlockCalledNotExternallyDisabled() {
+ logBuffer.log(TAG, WARNING, "verifyUnlock called when not externally disabled")
+ }
+
+ fun logSetOccluded(isOccluded: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isOccluded },
+ { "setOccluded($bool1)" }
+ )
+ }
+
+ fun logHandleSetOccluded(isOccluded: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isOccluded },
+ { "handleSetOccluded($bool1)" }
+ )
+ }
+
+ fun logIgnoreHandleShow() {
+ logBuffer.log(TAG, DEBUG, "ignoring handleShow because system is not ready.")
+ }
+
+ fun logHandleShow() {
+ logBuffer.log(TAG, DEBUG, "handleShow")
+ }
+
+ fun logHandleHide() {
+ logBuffer.log(TAG, DEBUG, "handleHide")
+ }
+
+ fun logSplitSystemUserQuitUnlocking() {
+ logBuffer.log(TAG, DEBUG, "Split system user, quit unlocking.")
+ }
+
+ fun logHandleStartKeyguardExitAnimation(startTime: Long, fadeoutDuration: Long) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ long1 = startTime
+ long2 = fadeoutDuration
+ },
+ { "handleStartKeyguardExitAnimation startTime=$long1 fadeoutDuration=$long2" }
+ )
+ }
+
+ fun logHandleVerifyUnlock() {
+ logBuffer.log(TAG, DEBUG, "handleVerifyUnlock")
+ }
+
+ fun logHandleNotifyStartedGoingToSleep() {
+ logBuffer.log(TAG, DEBUG, "handleNotifyStartedGoingToSleep")
+ }
+
+ fun logHandleNotifyFinishedGoingToSleep() {
+ logBuffer.log(TAG, DEBUG, "handleNotifyFinishedGoingToSleep")
+ }
+
+ fun logHandleNotifyWakingUp() {
+ logBuffer.log(TAG, DEBUG, "handleNotifyWakingUp")
+ }
+
+ fun logHandleReset() {
+ logBuffer.log(TAG, DEBUG, "handleReset")
+ }
+
+ fun logKeyguardDone() {
+ logBuffer.log(TAG, DEBUG, "keyguardDone")
+ }
+
+ fun logKeyguardDonePending() {
+ logBuffer.log(TAG, DEBUG, "keyguardDonePending")
+ }
+
+ fun logKeyguardGone() {
+ logBuffer.log(TAG, DEBUG, "keyguardGone")
+ }
+
+ fun logUnoccludeAnimationCancelled(isOccluded: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isOccluded },
+ { "Unocclude animation cancelled. Occluded state is now: $bool1" }
+ )
+ }
+
+ fun logShowLocked() {
+ logBuffer.log(TAG, DEBUG, "showLocked")
+ }
+
+ fun logHideLocked() {
+ logBuffer.log(TAG, DEBUG, "hideLocked")
+ }
+
+ fun logResetStateLocked() {
+ logBuffer.log(TAG, DEBUG, "resetStateLocked")
+ }
+
+ fun logNotifyStartedGoingToSleep() {
+ logBuffer.log(TAG, DEBUG, "notifyStartedGoingToSleep")
+ }
+
+ fun logNotifyFinishedGoingToSleep() {
+ logBuffer.log(TAG, DEBUG, "notifyFinishedGoingToSleep")
+ }
+
+ fun logNotifyStartedWakingUp() {
+ logBuffer.log(TAG, DEBUG, "notifyStartedWakingUp")
+ }
+
+ fun logDoKeyguardShowingLockScreen() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: showing the lock screen")
+ }
+
+ fun logDoKeyguardNotShowingLockScreenOff() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because lockscreen is off")
+ }
+
+ fun logDoKeyguardNotShowingDeviceNotProvisioned() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because device isn't " +
+ "provisioned and the sim is not locked or missing")
+ }
+
+ fun logDoKeyguardNotShowingAlreadyShowing() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because it is already showing")
+ }
+
+ fun logDoKeyguardNotShowingBootingCryptkeeper() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because booting to cryptkeeper")
+ }
+
+ fun logDoKeyguardNotShowingExternallyDisabled() {
+ logBuffer.log(TAG, DEBUG, "doKeyguard: not showing because externally disabled")
+ }
+
+ fun logFailedToCallOnDeviceProvisioned(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onDeviceProvisioned",
+ remoteException
+ )
+ }
+
+ fun logMaybeHandlePendingLockNotHandling() {
+ logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: not handling because the " +
+ "screen off animation's isKeyguardShowDelayed() returned true. This should be " +
+ "handled soon by #onStartedWakingUp, or by the end actions of the " +
+ "screen off animation.")
+ }
+
+ fun logMaybeHandlePendingLockKeyguardGoingAway() {
+ logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: not handling because the " +
+ "keyguard is going away. This should be handled shortly by " +
+ "StatusBar#finishKeyguardFadingAway.")
+ }
+
+ fun logMaybeHandlePendingLockHandling() {
+ logBuffer.log(TAG, DEBUG, "#maybeHandlePendingLock: handling pending lock; " +
+ "locking keyguard.")
+ }
+
+ fun logSetAlarmToTurnOffKeyguard(delayedShowingSequence: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = delayedShowingSequence },
+ { "setting alarm to turn off keyguard, seq = $int1" }
+ )
+ }
+
+ fun logOnStartedWakingUp(delayedShowingSequence: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = delayedShowingSequence },
+ { "onStartedWakingUp, seq = $int1" }
+ )
+ }
+
+ fun logSetKeyguardEnabled(enabled: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = enabled },
+ { "setKeyguardEnabled($bool1)" }
+ )
+ }
+
+ fun logIgnoreVerifyUnlockRequest() {
+ logBuffer.log(TAG, DEBUG, "in process of verifyUnlock request, ignoring")
+ }
+
+ fun logRememberToReshowLater() {
+ logBuffer.log(TAG, DEBUG, "remembering to reshow, hiding keyguard, disabling " +
+ "status bar expansion")
+ }
+
+ fun logPreviouslyHiddenReshow() {
+ logBuffer.log(TAG, DEBUG, "previously hidden, reshowing, reenabling status " +
+ "bar expansion")
+ }
+
+ fun logOnKeyguardExitResultFalseResetting() {
+ logBuffer.log(TAG, DEBUG, "onKeyguardExitResult(false), resetting")
+ }
+
+ fun logWaitingUntilKeyguardVisibleIsFalse() {
+ logBuffer.log(TAG, DEBUG, "waiting until mWaitingUntilKeyguardVisible is false")
+ }
+
+ fun logDoneWaitingUntilKeyguardVisible() {
+ logBuffer.log(TAG, DEBUG, "done waiting for mWaitingUntilKeyguardVisible")
+ }
+
+ fun logUnoccludeAnimatorOnAnimationStart() {
+ logBuffer.log(TAG, DEBUG, "UnoccludeAnimator#onAnimationStart. " +
+ "Set occluded = false.")
+ }
+
+ fun logNoAppsProvidedToUnoccludeRunner() {
+ logBuffer.log(TAG, DEBUG, "No apps provided to unocclude runner; " +
+ "skipping animation and unoccluding.")
+ }
+
+ fun logReceivedDelayedKeyguardAction(sequence: Int, delayedShowingSequence: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = sequence
+ int2 = delayedShowingSequence
+ },
+ {
+ "received DELAYED_KEYGUARD_ACTION with seq = $int1 " +
+ "mDelayedShowingSequence = $int2"
+ }
+ )
+ }
+
+ fun logTimeoutWhileActivityDrawn() {
+ logBuffer.log(TAG, WARNING, "Timeout while waiting for activity drawn")
+ }
+
+ fun logTryKeyguardDonePending(
+ keyguardDonePending: Boolean,
+ hideAnimationRun: Boolean,
+ hideAnimationRunning: Boolean
+ ) {
+ logBuffer.log(TAG, DEBUG,
+ {
+ bool1 = keyguardDonePending
+ bool2 = hideAnimationRun
+ bool3 = hideAnimationRunning
+ },
+ { "tryKeyguardDone: pending - $bool1, animRan - $bool2 animRunning - $bool3" }
+ )
+ }
+
+ fun logTryKeyguardDonePreHideAnimation() {
+ logBuffer.log(TAG, DEBUG, "tryKeyguardDone: starting pre-hide animation")
+ }
+
+ fun logHandleKeyguardDone() {
+ logBuffer.log(TAG, DEBUG, "handleKeyguardDone")
+ }
+
+ fun logDeviceGoingToSleep() {
+ logBuffer.log(TAG, INFO, "Device is going to sleep, aborting keyguardDone")
+ }
+
+ fun logFailedToCallOnKeyguardExitResultTrue(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onKeyguardExitResult(true)",
+ remoteException
+ )
+ }
+
+ fun logHandleKeyguardDoneDrawing() {
+ logBuffer.log(TAG, DEBUG, "handleKeyguardDoneDrawing")
+ }
+
+ fun logHandleKeyguardDoneDrawingNotifyingKeyguardVisible() {
+ logBuffer.log(TAG, DEBUG, "handleKeyguardDoneDrawing: notifying " +
+ "mWaitingUntilKeyguardVisible")
+ }
+
+ fun logUpdateActivityLockScreenState(showing: Boolean, aodShowing: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = showing
+ bool2 = aodShowing
+ },
+ { "updateActivityLockScreenState($bool1, $bool2)" }
+ )
+ }
+
+ fun logFailedToCallSetLockScreenShown(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call setLockScreenShown",
+ remoteException
+ )
+ }
+
+ fun logKeyguardGoingAway() {
+ logBuffer.log(TAG, DEBUG, "keyguardGoingAway")
+ }
+
+ fun logFailedToCallKeyguardGoingAway(keyguardFlag: Int, remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ ERROR,
+ { int1 = keyguardFlag },
+ { "Failed to call keyguardGoingAway($int1)" },
+ remoteException
+ )
+ }
+
+ fun logHideAnimationFinishedRunnable() {
+ logBuffer.log(TAG, WARNING, "mHideAnimationFinishedRunnable#run")
+ }
+
+ fun logFailedToCallOnAnimationFinished(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onAnimationFinished",
+ remoteException
+ )
+ }
+
+ fun logFailedToCallOnAnimationStart(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onAnimationStart",
+ remoteException
+ )
+ }
+
+ fun logOnKeyguardExitRemoteAnimationFinished() {
+ logBuffer.log(TAG, DEBUG, "onKeyguardExitRemoteAnimationFinished")
+ }
+
+ fun logSkipOnKeyguardExitRemoteAnimationFinished(
+ cancelled: Boolean,
+ surfaceBehindRemoteAnimationRunning: Boolean,
+ surfaceBehindRemoteAnimationRequested: Boolean
+ ) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = cancelled
+ bool2 = surfaceBehindRemoteAnimationRunning
+ bool3 = surfaceBehindRemoteAnimationRequested
+ },
+ {
+ "skip onKeyguardExitRemoteAnimationFinished cancelled=$bool1 " +
+ "surfaceAnimationRunning=$bool2 " +
+ "surfaceAnimationRequested=$bool3"
+ }
+ )
+ }
+
+ fun logOnKeyguardExitRemoteAnimationFinishedHideKeyguardView() {
+ logBuffer.log(TAG, DEBUG, "onKeyguardExitRemoteAnimationFinished" +
+ "#hideKeyguardViewAfterRemoteAnimation")
+ }
+
+ fun logSkipHideKeyguardViewAfterRemoteAnimation(
+ dismissingFromSwipe: Boolean,
+ wasShowing: Boolean
+ ) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = dismissingFromSwipe
+ bool2 = wasShowing
+ },
+ {
+ "skip hideKeyguardViewAfterRemoteAnimation dismissFromSwipe=$bool1 " +
+ "wasShowing=$bool2"
+ }
+ )
+ }
+
+ fun logCouldNotGetStatusBarManager() {
+ logBuffer.log(TAG, WARNING, "Could not get status bar manager")
+ }
+
+ fun logAdjustStatusBarLocked(
+ showing: Boolean,
+ occluded: Boolean,
+ secure: Boolean,
+ forceHideHomeRecentsButtons: Boolean,
+ flags: String
+ ) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = showing
+ bool2 = occluded
+ bool3 = secure
+ bool4 = forceHideHomeRecentsButtons
+ str3 = flags
+ },
+ {
+ "adjustStatusBarLocked: mShowing=$bool1 mOccluded=$bool2 isSecure=$bool3 " +
+ "force=$bool4 --> flags=0x$str3"
+ }
+ )
+ }
+
+ fun logFailedToCallOnShowingStateChanged(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call onShowingStateChanged",
+ remoteException
+ )
+ }
+
+ fun logFailedToCallNotifyTrustedChangedLocked(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call notifyTrustedChangedLocked",
+ remoteException
+ )
+ }
+
+ fun logFailedToCallIKeyguardStateCallback(remoteException: RemoteException) {
+ logBuffer.log(
+ TAG,
+ WARNING,
+ "Failed to call to IKeyguardStateCallback",
+ remoteException
+ )
+ }
+
+ fun logOccludeAnimatorOnAnimationStart() {
+ logBuffer.log(TAG, DEBUG, "OccludeAnimator#onAnimationStart. Set occluded = true.")
+ }
+
+ fun logOccludeAnimationCancelledByWm(isKeyguardOccluded: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isKeyguardOccluded },
+ { "Occlude animation cancelled by WM. Setting occluded state to: $bool1" }
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 589ec0e72b3b..9b5f54a0a91d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -92,7 +92,7 @@ open class AuthBiometricFingerprintIconController(
STATE_ERROR -> true
STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
- STATE_AUTHENTICATED -> false
+ STATE_AUTHENTICATED -> true
else -> false
}
@@ -114,7 +114,13 @@ open class AuthBiometricFingerprintIconController(
R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
}
}
- STATE_AUTHENTICATED -> R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ STATE_AUTHENTICATED -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ R.raw.fingerprint_dialogue_error_to_success_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+ }
+ }
else -> return null
}
return if (id != null) return id else null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
index 31baa0ff1154..9cce066afe9d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -75,7 +75,7 @@ open class AuthBiometricFingerprintView(
}
}
- override fun getDelayAfterAuthenticatedDurationMs() = 0
+ override fun getDelayAfterAuthenticatedDurationMs() = 500
override fun getStateForAfterError() = STATE_AUTHENTICATING
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e866b9c0bb25..fc5cf9f005ed 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -468,6 +468,7 @@ public abstract class AuthBiometricView extends LinearLayout {
break;
case STATE_AUTHENTICATED:
+ removePendingAnimations();
if (mSize != AuthDialog.SIZE_SMALL) {
mConfirmButton.setVisibility(View.GONE);
mNegativeButton.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index 83249aa324d1..bbcab60d7ba2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -69,7 +69,7 @@ public class ComplicationTypesUpdater extends CoreStartable {
};
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
settingsObserver,
UserHandle.myUserId());
settingsObserver.onChange(false);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 677f0a2e288b..29d67650fbd8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -155,10 +155,10 @@ public class Flags {
new ReleasedFlag(603, false);
public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_BACKEND =
- new UnreleasedFlag(604, true);
+ new UnreleasedFlag(604, false);
public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_FRONTEND =
- new UnreleasedFlag(605, true);
+ new UnreleasedFlag(605, false);
/***************************************/
// 700 - dialer/calls
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2c60d5d67dac..70e0d5fa9ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -73,8 +73,6 @@ import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
@@ -106,6 +104,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
@@ -513,8 +512,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onKeyguardVisibilityChanged(boolean showing) {
synchronized (KeyguardViewMediator.this) {
if (!showing && mPendingPinLock) {
- Log.i(TAG, "PIN lock requested, starting keyguard");
-
+ mLogger.logPinLockRequestedStartingKeyguard();
// Bring the keyguard back in order to show the PIN lock
mPendingPinLock = false;
doKeyguardLocked(null);
@@ -524,7 +522,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void onUserSwitching(int userId) {
- if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
+ mLogger.logUserSwitching(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.
@@ -542,7 +540,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void onUserSwitchComplete(int userId) {
- if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
+ mLogger.logOnUserSwitchComplete(userId);
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
// Don't try to dismiss if the user has Pin/Pattern/Password set
@@ -570,8 +568,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onSimStateChanged(int subId, int slotId, int simState) {
if (DEBUG_SIM_STATES) {
- Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
- + ",state=" + simState + ")");
+ mLogger.logOnSimStateChanged(subId, slotId, String.valueOf(simState));
}
int size = mKeyguardStateCallbacks.size();
@@ -580,7 +577,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
mKeyguardStateCallbacks.get(i).onSimSecureStateChanged(simPinSecure);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onSimSecureStateChanged", e);
+ mLogger.logFailedToCallOnSimSecureStateChanged(e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
}
@@ -603,9 +600,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
if (shouldWaitForProvisioning()) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing,"
- + " we need to show the keyguard since the "
- + "device isn't provisioned yet.");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logIccAbsentIsNotShowing();
+ }
doKeyguardLocked(null);
} else {
resetStateLocked();
@@ -615,8 +612,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
if (lastSimStateWasLocked) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
- + "previous state was locked. Reset the state.");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logSimMovedToAbsent();
+ }
resetStateLocked();
}
mSimWasLocked.append(slotId, false);
@@ -629,9 +627,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSimWasLocked.append(slotId, true);
mPendingPinLock = true;
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG,
- "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
- + "showing; need to show keyguard so user can enter sim pin");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logIntentValueIccLocked();
+ }
doKeyguardLocked(null);
} else {
resetStateLocked();
@@ -641,29 +639,36 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
case TelephonyManager.SIM_STATE_PERM_DISABLED:
synchronized (KeyguardViewMediator.this) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and "
- + "keygaurd isn't showing.");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logPermDisabledKeyguardNotShowing();
+ }
doKeyguardLocked(null);
} else {
- if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
- + "show permanently disabled message in lockscreen.");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logPermDisabledResetStateLocked();
+ }
resetStateLocked();
}
}
break;
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+ if (DEBUG_SIM_STATES) {
+ mLogger.logReadyResetState(mShowing);
+ }
if (mShowing && mSimWasLocked.get(slotId, false)) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
- + "previously was locked. Reset the state.");
+ if (DEBUG_SIM_STATES) {
+ mLogger.logSimMovedToReady();
+ }
mSimWasLocked.append(slotId, false);
resetStateLocked();
}
}
break;
default:
- if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
+ if (DEBUG_SIM_STATES) {
+ mLogger.logUnspecifiedSimState(simState);
+ }
break;
}
}
@@ -708,7 +713,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}
- if (DEBUG) Log.d(TAG, "keyguardDone");
+ mLogger.logKeyguardDone();
tryKeyguardDone();
}
@@ -727,7 +732,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void keyguardDonePending(boolean strongAuth, int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
- if (DEBUG) Log.d(TAG, "keyguardDonePending");
+ mLogger.logKeyguardDonePending();
if (targetUserId != ActivityManager.getCurrentUser()) {
Trace.endSection();
return;
@@ -746,7 +751,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void keyguardGone() {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
- if (DEBUG) Log.d(TAG, "keyguardGone");
+ mLogger.logKeyguardGone();
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false);
mKeyguardDisplayManager.hide();
Trace.endSection();
@@ -832,8 +837,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void onLaunchAnimationCancelled() {
- Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
- + mOccluded);
+ mLogger.logOccludeLaunchAnimationCancelled(mOccluded);
}
@Override
@@ -853,8 +857,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void setLaunchContainer(@NonNull ViewGroup launchContainer) {
// No-op, launch container is always the shade.
- Log.wtf(TAG, "Someone tried to change the launch container for the "
- + "ActivityLaunchAnimator, which should never happen.");
+ mLogger.logActivityLaunchAnimatorLaunchContainerChanged();
}
@NonNull
@@ -905,8 +908,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
- Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: "
- + mOccluded);
+ mLogger.logUnoccludeAnimationCancelled(mOccluded);
}
@Override
@@ -914,12 +916,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- Log.d(TAG, "UnoccludeAnimator#onAnimationStart. Set occluded = false.");
+ mLogger.logUnoccludeAnimatorOnAnimationStart();
setOccluded(false /* isOccluded */, true /* animate */);
if (apps == null || apps.length == 0 || apps[0] == null) {
- Log.d(TAG, "No apps provided to unocclude runner; "
- + "skipping animation and unoccluding.");
+ mLogger.logNoAppsProvidedToUnoccludeRunner();
finishedCallback.onAnimationFinished();
return;
}
@@ -1007,6 +1008,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private ScreenOnCoordinator mScreenOnCoordinator;
private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
+ private KeyguardViewMediatorLogger mLogger;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -1035,7 +1037,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ KeyguardViewMediatorLogger logger) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -1078,6 +1081,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mDreamOverlayStateController = dreamOverlayStateController;
mActivityLaunchAnimator = activityLaunchAnimator;
+ mLogger = logger;
mPowerButtonY = context.getResources().getDimensionPixelSize(
R.dimen.physical_power_button_center_screen_location_y);
@@ -1141,21 +1145,21 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mLockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mLockSoundId == 0) {
- Log.w(TAG, "failed to load lock sound from " + soundPath);
+ mLogger.logFailedLoadLockSound(soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
if (soundPath != null) {
mUnlockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mUnlockSoundId == 0) {
- Log.w(TAG, "failed to load unlock sound from " + soundPath);
+ mLogger.logFailedLoadUnlockSound(soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
if (soundPath != null) {
mTrustedSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mTrustedSoundId == 0) {
- Log.w(TAG, "failed to load trusted sound from " + soundPath);
+ mLogger.logFailedLoadTrustedSound(soundPath);
}
int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
@@ -1184,7 +1188,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleSystemReady() {
synchronized (this) {
- if (DEBUG) Log.d(TAG, "onSystemReady");
+ mLogger.logOnSystemReady();
mSystemReady = true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
@@ -1202,7 +1206,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* {@link WindowManagerPolicyConstants#OFF_BECAUSE_OF_TIMEOUT}.
*/
public void onStartedGoingToSleep(@WindowManagerPolicyConstants.OffReason int offReason) {
- if (DEBUG) Log.d(TAG, "onStartedGoingToSleep(" + offReason + ")");
+ mLogger.logOnStartedGoingToSleep(offReason);
synchronized (this) {
mDeviceInteractive = false;
mPowerGestureIntercepted = false;
@@ -1218,11 +1222,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
mLockLater = false;
if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
+ mLogger.logPendingExitSecureCallbackCancelled();
try {
mExitSecureCallback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedOnKeyguardExitResultFalse(e);
}
mExitSecureCallback = null;
if (!mExternallyEnabled) {
@@ -1267,7 +1271,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
public void onFinishedGoingToSleep(
@WindowManagerPolicyConstants.OffReason int offReason, boolean cameraGestureTriggered) {
- if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + offReason + ")");
+ mLogger.logOnFinishedGoingToSleep(offReason);
synchronized (this) {
mDeviceInteractive = false;
mGoingToSleep = false;
@@ -1325,13 +1329,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// - The screen off animation is cancelled by the device waking back up. We will call
// maybeHandlePendingLock from KeyguardViewMediator#onStartedWakingUp.
if (mScreenOffAnimationController.isKeyguardShowDelayed()) {
- if (DEBUG) {
- Log.d(TAG, "#maybeHandlePendingLock: not handling because the screen off "
- + "animation's isKeyguardShowDelayed() returned true. This should be "
- + "handled soon by #onStartedWakingUp, or by the end actions of the "
- + "screen off animation.");
- }
-
+ mLogger.logMaybeHandlePendingLockNotHandling();
return;
}
@@ -1341,18 +1339,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// StatusBar#finishKeyguardFadingAway, which is always responsible for setting
// isKeyguardGoingAway to false.
if (mKeyguardStateController.isKeyguardGoingAway()) {
- if (DEBUG) {
- Log.d(TAG, "#maybeHandlePendingLock: not handling because the keyguard is "
- + "going away. This should be handled shortly by "
- + "StatusBar#finishKeyguardFadingAway.");
- }
-
+ mLogger.logMaybeHandlePendingLockKeyguardGoingAway();
return;
}
- if (DEBUG) {
- Log.d(TAG, "#maybeHandlePendingLock: handling pending lock; locking keyguard.");
- }
+ mLogger.logMaybeHandlePendingLockHandling();
doKeyguardLocked(null);
setPendingLock(false);
@@ -1421,8 +1412,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
PendingIntent sender = PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
- if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
- + mDelayedShowingSequence);
+ mLogger.logSetAlarmToTurnOffKeyguard(mDelayedShowingSequence);
doKeyguardLaterForChildProfilesLocked();
}
@@ -1482,7 +1472,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mAnimatingScreenOff = false;
cancelDoKeyguardLaterLocked();
cancelDoKeyguardForChildProfilesLocked();
- if (DEBUG) Log.d(TAG, "onStartedWakingUp, seq = " + mDelayedShowingSequence);
+ mLogger.logOnStartedWakingUp(mDelayedShowingSequence);
notifyStartedWakingUp();
}
mUpdateMonitor.dispatchStartedWakingUp();
@@ -1542,37 +1532,35 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
public void setKeyguardEnabled(boolean enabled) {
synchronized (this) {
- if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
+ mLogger.logSetKeyguardEnabled(enabled);
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
+ mLogger.logIgnoreVerifyUnlockRequest();
// we're in the process of handling a request to verify the user
// can get past the keyguard. ignore extraneous requests to disable / re-enable
return;
}
// hiding keyguard that is showing, remember to reshow later
- if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
- + "disabling status bar expansion");
+ mLogger.logRememberToReshowLater();
mNeedToReshowWhenReenabled = true;
updateInputRestrictedLocked();
hideLocked();
} else if (enabled && mNeedToReshowWhenReenabled) {
// re-enabled after previously hidden, reshow
- if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
- + "status bar expansion");
+ mLogger.logPreviouslyHiddenReshow();
mNeedToReshowWhenReenabled = false;
updateInputRestrictedLocked();
if (mExitSecureCallback != null) {
- if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
+ mLogger.logOnKeyguardExitResultFalseResetting();
try {
mExitSecureCallback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
mExitSecureCallback = null;
resetStateLocked();
@@ -1584,7 +1572,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// and causing an ANR).
mWaitingUntilKeyguardVisible = true;
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
- if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
+ mLogger.logWaitingUntilKeyguardVisibleIsFalse();
while (mWaitingUntilKeyguardVisible) {
try {
wait();
@@ -1592,7 +1580,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Thread.currentThread().interrupt();
}
}
- if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
+ mLogger.logDoneWaitingUntilKeyguardVisible();
}
}
}
@@ -1604,31 +1592,31 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void verifyUnlock(IKeyguardExitCallback callback) {
Trace.beginSection("KeyguardViewMediator#verifyUnlock");
synchronized (this) {
- if (DEBUG) Log.d(TAG, "verifyUnlock");
+ mLogger.logVerifyUnlock();
if (shouldWaitForProvisioning()) {
// don't allow this api when the device isn't provisioned
- if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
+ mLogger.logIgnoreUnlockDeviceNotProvisioned();
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
} else if (mExternallyEnabled) {
// this only applies when the user has externally disabled the
// keyguard. this is unexpected and means the user is not
// using the api properly.
- Log.w(TAG, "verifyUnlock called when not externally disabled");
+ mLogger.logVerifyUnlockCalledNotExternallyDisabled();
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
} else if (mExitSecureCallback != null) {
// already in progress with someone else
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
} else if (!isSecure()) {
@@ -1640,7 +1628,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
callback.onKeyguardExitResult(true);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
} else {
@@ -1649,7 +1637,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
callback.onKeyguardExitResult(false);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ mLogger.logFailedToCallOnKeyguardExitResultFalse(e);
}
}
}
@@ -1667,10 +1655,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* Notify us when the keyguard is occluded by another window
*/
public void setOccluded(boolean isOccluded, boolean animate) {
- Log.d(TAG, "setOccluded(" + isOccluded + ")");
-
Trace.beginSection("KeyguardViewMediator#setOccluded");
- if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
+ mLogger.logSetOccluded(isOccluded);
mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD);
mHandler.removeMessages(SET_OCCLUDED);
Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0);
@@ -1699,7 +1685,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
private void handleSetOccluded(boolean isOccluded, boolean animate) {
Trace.beginSection("KeyguardViewMediator#handleSetOccluded");
- Log.d(TAG, "handleSetOccluded(" + isOccluded + ")");
+ mLogger.logHandleSetOccluded(isOccluded);
synchronized (KeyguardViewMediator.this) {
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
@@ -1756,7 +1742,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
callback.onInputRestrictedStateChanged(inputRestricted);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onDeviceProvisioned", e);
+ mLogger.logFailedToCallOnDeviceProvisioned(e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(callback);
}
@@ -1771,8 +1757,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
if (!mExternallyEnabled) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
-
+ mLogger.logDoKeyguardNotShowingExternallyDisabled();
mNeedToReshowWhenReenabled = true;
return;
}
@@ -1781,7 +1766,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// to account for the hiding animation which results in a delay and discrepancy
// between flags
if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ mLogger.logDoKeyguardNotShowingAlreadyShowing();
resetStateLocked();
return;
}
@@ -1800,20 +1785,19 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
|| ((absent || disabled) && requireSim);
if (!lockedOrMissing && shouldWaitForProvisioning()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
- + " and the sim is not locked or missing");
+ mLogger.logDoKeyguardNotShowingDeviceNotProvisioned();
return;
}
boolean forceShow = options != null && options.getBoolean(OPTION_FORCE_SHOW, false);
if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
&& !lockedOrMissing && !forceShow) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+ mLogger.logDoKeyguardNotShowingLockScreenOff();
return;
}
}
- if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+ mLogger.logDoKeyguardShowingLockScreen();
showLocked(options);
}
@@ -1851,32 +1835,23 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* @see #handleReset
*/
private void resetStateLocked() {
- if (DEBUG) Log.e(TAG, "resetStateLocked");
+ mLogger.logResetStateLocked();
Message msg = mHandler.obtainMessage(RESET);
mHandler.sendMessage(msg);
}
- /**
- * Send message to keyguard telling it to verify unlock
- * @see #handleVerifyUnlock()
- */
- private void verifyUnlockLocked() {
- if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
- mHandler.sendEmptyMessage(VERIFY_UNLOCK);
- }
-
private void notifyStartedGoingToSleep() {
- if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
+ mLogger.logNotifyStartedGoingToSleep();
mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
}
private void notifyFinishedGoingToSleep() {
- if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep");
+ mLogger.logNotifyFinishedGoingToSleep();
mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP);
}
private void notifyStartedWakingUp() {
- if (DEBUG) Log.d(TAG, "notifyStartedWakingUp");
+ mLogger.logNotifyStartedWakingUp();
mHandler.sendEmptyMessage(NOTIFY_STARTED_WAKING_UP);
}
@@ -1886,7 +1861,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
private void showLocked(Bundle options) {
Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock");
- if (DEBUG) Log.d(TAG, "showLocked");
+ mLogger.logShowLocked();
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW, options);
@@ -1903,7 +1878,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
private void hideLocked() {
Trace.beginSection("KeyguardViewMediator#hideLocked");
- if (DEBUG) Log.d(TAG, "hideLocked");
+ mLogger.logHideLocked();
Message msg = mHandler.obtainMessage(HIDE);
mHandler.sendMessage(msg);
Trace.endSection();
@@ -1982,8 +1957,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onReceive(Context context, Intent intent) {
if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
final int sequence = intent.getIntExtra("seq", 0);
- if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
- + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
+ mLogger.logReceivedDelayedKeyguardAction(sequence, mDelayedShowingSequence);
synchronized (KeyguardViewMediator.this) {
if (mDelayedShowingSequence == sequence) {
doKeyguardLocked(null);
@@ -2016,7 +1990,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void keyguardDone() {
Trace.beginSection("KeyguardViewMediator#keyguardDone");
- if (DEBUG) Log.d(TAG, "keyguardDone()");
+ mLogger.logKeyguardDone();
userActivity();
EventLog.writeEvent(70000, 2);
Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
@@ -2108,7 +2082,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
case KEYGUARD_DONE_PENDING_TIMEOUT:
Trace.beginSection("KeyguardViewMediator#handleMessage"
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
- Log.w(TAG, "Timeout while waiting for activity drawn!");
+ mLogger.logTimeoutWhileActivityDrawn();
Trace.endSection();
break;
case SYSTEM_READY:
@@ -2119,14 +2093,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
};
private void tryKeyguardDone() {
- if (DEBUG) {
- Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
- + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
- }
+ mLogger.logTryKeyguardDonePending(
+ mKeyguardDonePending,
+ mHideAnimationRun,
+ mHideAnimationRunning
+ );
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
- if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
+ mLogger.logTryKeyguardDonePreHideAnimation();
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
@@ -2146,14 +2121,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
});
- if (DEBUG) Log.d(TAG, "handleKeyguardDone");
+ mLogger.logHandleKeyguardDone();
synchronized (this) {
resetKeyguardDonePendingLocked();
}
if (mGoingToSleep) {
mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
- Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
+ mLogger.logDeviceGoingToSleep();
return;
}
setPendingLock(false); // user may have authenticated during the screen off animation
@@ -2161,7 +2136,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
mExitSecureCallback.onKeyguardExitResult(true /* authenciated */);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onKeyguardExitResult()", e);
+ mLogger.logFailedToCallOnKeyguardExitResultTrue(e);
}
mExitSecureCallback = null;
@@ -2204,9 +2179,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleKeyguardDoneDrawing() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDoneDrawing");
synchronized(this) {
- if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing");
+ mLogger.logHandleKeyguardDoneDrawing();
if (mWaitingUntilKeyguardVisible) {
- if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
+ mLogger.logHandleKeyguardDoneDrawingNotifyingKeyguardVisible();
mWaitingUntilKeyguardVisible = false;
notifyAll();
@@ -2256,12 +2231,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
mUiBgExecutor.execute(() -> {
- if (DEBUG) {
- Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
- }
+ mLogger.logUpdateActivityLockScreenState(showing, aodShowing);
try {
ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
} catch (RemoteException e) {
+ mLogger.logFailedToCallSetLockScreenShown(e);
}
});
}
@@ -2278,10 +2252,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
- if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
+ mLogger.logIgnoreHandleShow();
return;
} else {
- if (DEBUG) Log.d(TAG, "handleShow");
+ mLogger.logHandleShow();
}
mHiding = false;
@@ -2313,7 +2287,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void run() {
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
- if (DEBUG) Log.d(TAG, "keyguardGoingAway");
+ mLogger.logKeyguardGoingAway();
mKeyguardViewControllerLazy.get().keyguardGoingAway();
int flags = 0;
@@ -2357,7 +2331,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
} catch (RemoteException e) {
- Log.e(TAG, "Error while calling WindowManager", e);
+ mLogger.logFailedToCallKeyguardGoingAway(keyguardFlag, e);
}
});
Trace.endSection();
@@ -2365,7 +2339,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
};
private final Runnable mHideAnimationFinishedRunnable = () -> {
- Log.e(TAG, "mHideAnimationFinishedRunnable#run");
+ mLogger.logHideAnimationFinishedRunnable();
mHideAnimationRunning = false;
tryKeyguardDone();
};
@@ -2385,14 +2359,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleHide");
+ mLogger.logHandleHide();
if (mustNotUnlockCurrentUser()) {
// In split system user mode, we never unlock system user. The end user has to
// switch to another user.
// TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
// still completes and makes the screen blank.
- if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
+ mLogger.logSplitSystemUserQuitUnlocking();
mKeyguardExitAnimationRunner = null;
return;
}
@@ -2424,8 +2398,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
- Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
- + " fadeoutDuration=" + fadeoutDuration);
+ mLogger.logHandleStartKeyguardExitAnimation(startTime, fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
// Tell ActivityManager that we canceled the keyguard animation if
@@ -2441,7 +2414,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onAnimationFinished", e);
+ mLogger.logFailedToCallOnAnimationFinished(e);
}
}
setShowingLocked(mShowing, true /* force */);
@@ -2464,7 +2437,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onAnimationFinished", e);
+ mLogger.logFailedToCallOnAnimationFinished(e);
}
onKeyguardExitFinished();
mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
@@ -2483,7 +2456,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
wallpapers, nonApps, callback);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onAnimationStart", e);
+ mLogger.logFailedToCallOnAnimationStart(e);
}
// When remaining on the shade, there's no need to do a fancy remote animation,
@@ -2547,7 +2520,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- Slog.e(TAG, "RemoteException");
+ mLogger.logFailedToCallOnAnimationFinished(e);
} finally {
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@@ -2558,7 +2531,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
- Slog.e(TAG, "RemoteException");
+ mLogger.logFailedToCallOnAnimationFinished(e);
} finally {
mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@@ -2627,11 +2600,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
* @param cancelled {@code true} if the animation was cancelled before it finishes.
*/
public void onKeyguardExitRemoteAnimationFinished(boolean cancelled) {
- Log.d(TAG, "onKeyguardExitRemoteAnimationFinished");
+ mLogger.logOnKeyguardExitRemoteAnimationFinished();
if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
- Log.d(TAG, "skip onKeyguardExitRemoteAnimationFinished cancelled=" + cancelled
- + " surfaceAnimationRunning=" + mSurfaceBehindRemoteAnimationRunning
- + " surfaceAnimationRequested=" + mSurfaceBehindRemoteAnimationRequested);
+ mLogger.logSkipOnKeyguardExitRemoteAnimationFinished(cancelled, false, false);
return;
}
@@ -2645,13 +2616,13 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
onKeyguardExitFinished();
if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
- Log.d(TAG, "onKeyguardExitRemoteAnimationFinished"
- + "#hideKeyguardViewAfterRemoteAnimation");
+ mLogger.logOnKeyguardExitRemoteAnimationFinishedHideKeyguardView();
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
} else {
- Log.d(TAG, "skip hideKeyguardViewAfterRemoteAnimation"
- + " dismissFromSwipe=" + mKeyguardStateController.isDismissingFromSwipe()
- + " wasShowing=" + wasShowing);
+ mLogger.logSkipHideKeyguardViewAfterRemoteAnimation(
+ mKeyguardStateController.isDismissingFromSwipe(),
+ wasShowing
+ );
}
finishSurfaceBehindRemoteAnimation(cancelled);
@@ -2748,7 +2719,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
if (mStatusBarManager == null) {
- Log.w(TAG, "Could not get status bar manager");
+ mLogger.logCouldNotGetStatusBarManager();
} else {
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
@@ -2766,12 +2737,13 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
flags |= StatusBarManager.DISABLE_RECENT;
}
-
- if (DEBUG) {
- Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
- + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
- + " --> flags=0x" + Integer.toHexString(flags));
- }
+ mLogger.logAdjustStatusBarLocked(
+ mShowing,
+ mOccluded,
+ isSecure(),
+ forceHideHomeRecentsButtons,
+ Integer.toHexString(flags)
+ );
mStatusBarManager.disable(flags);
}
@@ -2783,7 +2755,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
private void handleReset() {
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleReset");
+ mLogger.logHandleReset();
mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */);
}
}
@@ -2795,7 +2767,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleVerifyUnlock() {
Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
+ mLogger.logHandleVerifyUnlock();
setShowingLocked(true);
mKeyguardViewControllerLazy.get().dismissAndCollapse();
}
@@ -2804,7 +2776,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleNotifyStartedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
+ mLogger.logHandleNotifyStartedGoingToSleep();
mKeyguardViewControllerLazy.get().onStartedGoingToSleep();
}
}
@@ -2815,7 +2787,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
*/
private void handleNotifyFinishedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyFinishedGoingToSleep");
+ mLogger.logHandleNotifyFinishedGoingToSleep();
mKeyguardViewControllerLazy.get().onFinishedGoingToSleep();
}
}
@@ -2823,7 +2795,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleNotifyStartedWakingUp() {
Trace.beginSection("KeyguardViewMediator#handleMotifyStartedWakingUp");
synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleNotifyWakingUp");
+ mLogger.logHandleNotifyWakingUp();
mKeyguardViewControllerLazy.get().onStartedWakingUp();
}
Trace.endSection();
@@ -3089,7 +3061,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser());
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onShowingStateChanged", e);
+ mLogger.logFailedToCallOnShowingStateChanged(e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(callback);
}
@@ -3108,7 +3080,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
mKeyguardStateCallbacks.get(i).onTrustedChanged(trusted);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call notifyTrustedChangedLocked", e);
+ mLogger.logFailedToCallNotifyTrustedChangedLocked(e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
}
@@ -3131,7 +3103,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust(
KeyguardUpdateMonitor.getCurrentUser()));
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e);
+ mLogger.logFailedToCallIKeyguardStateCallback(e);
}
}
}
@@ -3208,7 +3180,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// internal state to reflect that immediately, vs. waiting for the launch animator to
// begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to
// be occluded and might re-show the keyguard.
- Log.d(TAG, "OccludeAnimator#onAnimationStart. Set occluded = true.");
+ mLogger.logOccludeAnimatorOnAnimationStart();
setOccluded(true /* isOccluded */, false /* animate */);
}
@@ -3216,8 +3188,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onAnimationCancelled(boolean isKeyguardOccluded) throws RemoteException {
super.onAnimationCancelled(isKeyguardOccluded);
- Log.d(TAG, "Occlude animation cancelled by WM. "
- + "Setting occluded state to: " + isKeyguardOccluded);
+ mLogger.logOccludeAnimationCancelledByWm(isKeyguardOccluded);
setOccluded(isKeyguardOccluded /* occluded */, false /* animate */);
}
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 56f1ac46a875..fdea62dc0cbe 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.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
+import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -105,7 +106,8 @@ public class KeyguardModule {
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ KeyguardViewMediatorLogger logger) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -132,7 +134,8 @@ public class KeyguardModule {
interactionJankMonitor,
dreamOverlayStateController,
notificationShadeWindowController,
- activityLaunchAnimator);
+ activityLaunchAnimator,
+ logger);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 6124e10144f2..77ad8069f273 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -158,8 +158,13 @@ class LogBuffer @JvmOverloads constructor(
* add more detail to every log may do more to improve overall logging than adding more logs
* with this method.
*/
- fun log(tag: String, level: LogLevel, @CompileTimeConstant message: String) =
- log(tag, level, {str1 = message}, { str1!! })
+ fun log(
+ tag: String,
+ level: LogLevel,
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null
+ ) =
+ log(tag, level, {str1 = message}, { str1!! }, exception)
/**
* You should call [log] instead of this method.
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
index 323ee21953ea..684839f2b124 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
@@ -1,4 +1,7 @@
package com.android.systemui.log.dagger
+import javax.inject.Qualifier
+
/** A [com.android.systemui.log.LogBuffer] for KeyguardUpdateMonitor. */
+@Qualifier
annotation class KeyguardUpdateMonitorLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
new file mode 100644
index 000000000000..88e227b8ae35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardViewMediatorLog.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for KeyguardViewMediator. */
+@Qualifier
+annotation class KeyguardViewMediatorLog \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index c2a87649adef..9af42f825e00 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -305,4 +305,15 @@ public class LogModule {
public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
return factory.create("KeyguardUpdateMonitorLog", 200);
}
+
+ /**
+ * Provides a {@link LogBuffer} for use by
+ * {@link com.android.systemui.keyguard.KeyguardViewMediator}.
+ */
+ @Provides
+ @SysUISingleton
+ @KeyguardViewMediatorLog
+ public static LogBuffer provideKeyguardViewMediatorLogBuffer(LogBufferFactory factory) {
+ return factory.create("KeyguardViewMediatorLog", 100);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 6ac3eadb838d..7c4c64c20089 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -814,8 +814,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
mLogGesture = false;
String logPackageName = "";
+ Map<String, Integer> vocab = mVocab;
// Due to privacy, only top 100 most used apps by all users can be logged.
- if (mUseMLModel && mVocab.containsKey(mPackageName) && mVocab.get(mPackageName) < 100) {
+ if (mUseMLModel && vocab != null && vocab.containsKey(mPackageName)
+ && vocab.get(mPackageName) < 100) {
logPackageName = mPackageName;
}
SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 8b37aab87665..35f32caffe21 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -53,8 +53,7 @@ import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
-import androidx.annotation.NonNull;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
@@ -137,7 +136,7 @@ public class TakeScreenshotService extends Service {
}
@Override
- public IBinder onBind(@NonNull Intent intent) {
+ public IBinder onBind(Intent intent) {
registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS),
Context.RECEIVER_EXPORTED);
final Messenger m = new Messenger(mHandler);
@@ -184,13 +183,23 @@ public class TakeScreenshotService extends Service {
}
}
- /** Respond to incoming Message via Binder (Messenger) */
@MainThread
private boolean handleMessage(Message msg) {
final Messenger replyTo = msg.replyTo;
- final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);
- RequestCallback requestCallback = new RequestCallbackImpl(replyTo);
+ final Consumer<Uri> onSaved = (uri) -> reportUri(replyTo, uri);
+ RequestCallback callback = new RequestCallbackImpl(replyTo);
+
+ ScreenshotHelper.ScreenshotRequest request =
+ (ScreenshotHelper.ScreenshotRequest) msg.obj;
+ handleRequest(request, onSaved, callback);
+ return true;
+ }
+
+ @MainThread
+ @VisibleForTesting
+ void handleRequest(ScreenshotHelper.ScreenshotRequest request, Consumer<Uri> onSaved,
+ RequestCallback callback) {
// If the storage for this user is locked, we have no place to store
// the screenshot, so skip taking it instead of showing a misleading
// animation and error notification.
@@ -198,8 +207,8 @@ public class TakeScreenshotService extends Service {
Log.w(TAG, "Skipping screenshot because storage is locked!");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_user_locked_text);
- requestCallback.reportError();
- return true;
+ callback.reportError();
+ return;
}
if (mDevicePolicyManager.getScreenCaptureDisabled(null, UserHandle.USER_ALL)) {
@@ -211,33 +220,26 @@ public class TakeScreenshotService extends Service {
() -> mContext.getString(R.string.screenshot_blocked_by_admin));
mHandler.post(() ->
Toast.makeText(mContext, blockedByAdminText, Toast.LENGTH_SHORT).show());
- requestCallback.reportError();
+ callback.reportError();
});
- return true;
+ return;
}
- ScreenshotHelper.ScreenshotRequest screenshotRequest =
- (ScreenshotHelper.ScreenshotRequest) msg.obj;
-
- ComponentName topComponent = screenshotRequest.getTopComponent();
- mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
- topComponent == null ? "" : topComponent.getPackageName());
-
if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
Log.d(TAG, "handleMessage: Using request processor");
- mProcessor.processAsync(screenshotRequest,
- (request) -> dispatchToController(request, uriConsumer, requestCallback));
- return true;
+ mProcessor.processAsync(request,
+ (r) -> dispatchToController(r, onSaved, callback));
}
- dispatchToController(screenshotRequest, uriConsumer, requestCallback);
- return true;
+ dispatchToController(request, onSaved, callback);
}
private void dispatchToController(ScreenshotHelper.ScreenshotRequest request,
Consumer<Uri> uriConsumer, RequestCallback callback) {
ComponentName topComponent = request.getTopComponent();
+ mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(request.getSource()), 0,
+ topComponent == null ? "" : topComponent.getPackageName());
switch (request.getType()) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9ca09ed6d70e..29c7633ca93e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -97,6 +97,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.ActiveUnlockConfig;
+import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUnfoldTransition;
@@ -1415,19 +1416,10 @@ public final class NotificationPanelViewController extends PanelViewController {
private void updateClockAppearance() {
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
- final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean splitShadeWithActiveMedia =
- mSplitShadeEnabled && mMediaDataManager.hasActiveMediaOrRecommendation();
boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
- if ((hasVisibleNotifications && !mSplitShadeEnabled)
- || (splitShadeWithActiveMedia && !mDozing)) {
- mKeyguardStatusViewController.displayClock(SMALL, shouldAnimateClockChange);
- } else {
- mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
- }
- updateKeyguardStatusViewAlignment(true /* animate */);
+ mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
+ shouldAnimateClockChange);
+ updateKeyguardStatusViewAlignment(/* animate= */true);
int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
if (mKeyguardUserSwitcherController != null) {
@@ -1436,7 +1428,7 @@ public final class NotificationPanelViewController extends PanelViewController {
float expandedFraction =
mScreenOffAnimationController.shouldExpandNotifications()
? 1.0f : getExpandedFraction();
- float darkamount =
+ float darkAmount =
mScreenOffAnimationController.shouldExpandNotifications()
? 1.0f : mInterpolatedDarkAmount;
@@ -1454,7 +1446,7 @@ public final class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController.getLockscreenHeight(),
userSwitcherHeight,
userSwitcherPreferredY,
- darkamount, mOverStretchAmount,
+ darkAmount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayTopInset,
@@ -1486,6 +1478,34 @@ public final class NotificationPanelViewController extends PanelViewController {
updateClock();
}
+ @ClockSize
+ private int computeDesiredClockSize() {
+ if (mSplitShadeEnabled) {
+ return computeDesiredClockSizeForSplitShade();
+ }
+ return computeDesiredClockSizeForSingleShade();
+ }
+
+ @ClockSize
+ private int computeDesiredClockSizeForSingleShade() {
+ if (hasVisibleNotifications()) {
+ return SMALL;
+ }
+ return LARGE;
+ }
+
+ @ClockSize
+ private int computeDesiredClockSizeForSplitShade() {
+ // Media is not visible to the user on AOD.
+ boolean isMediaVisibleToUser =
+ mMediaDataManager.hasActiveMediaOrRecommendation() && !isOnAod();
+ if (isMediaVisibleToUser) {
+ // When media is visible, it overlaps with the large clock. Use small clock instead.
+ return SMALL;
+ }
+ return LARGE;
+ }
+
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
if (mStatusViewCentered != shouldBeCentered) {
@@ -1512,12 +1532,35 @@ public final class NotificationPanelViewController extends PanelViewController {
}
private boolean shouldKeyguardStatusViewBeCentered() {
- boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ if (mSplitShadeEnabled) {
+ return shouldKeyguardStatusViewBeCenteredInSplitShade();
+ }
+ return true;
+ }
+
+ private boolean shouldKeyguardStatusViewBeCenteredInSplitShade() {
+ if (!hasVisibleNotifications()) {
+ // No notifications visible. It is safe to have the clock centered as there will be no
+ // overlap.
+ return true;
+ }
+ if (hasPulsingNotifications()) {
+ // Pulsing notification appears on the right. Move clock left to avoid overlap.
+ return false;
+ }
+ // "Visible" notifications are actually not visible on AOD (unless pulsing), so it is safe
+ // to center the clock without overlap.
+ return isOnAod();
+ }
+
+ private boolean isOnAod() {
+ return mDozing && mDozeParameters.getAlwaysOn();
+ }
+
+ private boolean hasVisibleNotifications() {
+ return mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0
|| mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
- return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
- || hasPulsingNotifications();
}
/**
@@ -3789,6 +3832,8 @@ public final class NotificationPanelViewController extends PanelViewController {
mAnimateNextPositionUpdate = false;
}
mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
+
+ updateKeyguardStatusViewAlignment(/* animate= */ true);
}
public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 9e5dab1152ec..f8449ae8807b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -90,6 +90,18 @@ data class ListAttachState private constructor(
stableIndex = -1
}
+ /**
+ * Erases bookkeeping traces stored on an entry when it is removed from the notif list.
+ * This can happen if the entry is removed from a group that was broken up or if the entry was
+ * filtered out during any of the filtering steps.
+ */
+ fun detach() {
+ parent = null
+ section = null
+ promoter = null
+ // stableIndex = -1 // TODO(b/241229236): Clear this once we fix the stability fragility
+ }
+
companion object {
@JvmStatic
fun create(): ListAttachState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 14cc6bf1ea41..3eaa988e8389 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -958,9 +958,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* filtered out during any of the filtering steps.
*/
private void annulAddition(ListEntry entry) {
- entry.setParent(null);
- entry.getAttachState().setSection(null);
- entry.getAttachState().setPromoter(null);
+ entry.getAttachState().detach();
}
private void assignSections() {
@@ -1198,9 +1196,9 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
o2.getSectionIndex());
if (cmp != 0) return cmp;
- int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
- int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
- cmp = Integer.compare(index1, index2);
+ cmp = Integer.compare(
+ getStableOrderIndex(o1),
+ getStableOrderIndex(o2));
if (cmp != 0) return cmp;
NotifComparator sectionComparator = getSectionComparator(o1, o2);
@@ -1214,31 +1212,32 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
if (cmp != 0) return cmp;
}
- final NotificationEntry rep1 = o1.getRepresentativeEntry();
- final NotificationEntry rep2 = o2.getRepresentativeEntry();
- cmp = rep1.getRanking().getRank() - rep2.getRanking().getRank();
+ cmp = Integer.compare(
+ o1.getRepresentativeEntry().getRanking().getRank(),
+ o2.getRepresentativeEntry().getRanking().getRank());
if (cmp != 0) return cmp;
- cmp = Long.compare(
- rep2.getSbn().getNotification().when,
- rep1.getSbn().getNotification().when);
+ cmp = -1 * Long.compare(
+ o1.getRepresentativeEntry().getSbn().getNotification().when,
+ o2.getRepresentativeEntry().getSbn().getNotification().when);
return cmp;
};
private final Comparator<NotificationEntry> mGroupChildrenComparator = (o1, o2) -> {
- int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
- int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
- int cmp = Integer.compare(index1, index2);
+ int cmp = Integer.compare(
+ getStableOrderIndex(o1),
+ getStableOrderIndex(o2));
if (cmp != 0) return cmp;
- cmp = o1.getRepresentativeEntry().getRanking().getRank()
- - o2.getRepresentativeEntry().getRanking().getRank();
+ cmp = Integer.compare(
+ o1.getRepresentativeEntry().getRanking().getRank(),
+ o2.getRepresentativeEntry().getRanking().getRank());
if (cmp != 0) return cmp;
- cmp = Long.compare(
- o2.getRepresentativeEntry().getSbn().getNotification().when,
- o1.getRepresentativeEntry().getSbn().getNotification().when);
+ cmp = -1 * Long.compare(
+ o1.getRepresentativeEntry().getSbn().getNotification().when,
+ o2.getRepresentativeEntry().getSbn().getNotification().when);
return cmp;
};
@@ -1248,8 +1247,16 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
*/
private boolean mForceReorderable = false;
- private boolean canReorder(ListEntry entry) {
- return mForceReorderable || getStabilityManager().isEntryReorderingAllowed(entry);
+ private int getStableOrderIndex(ListEntry entry) {
+ if (mForceReorderable) {
+ // this is used to determine if the list is correctly sorted
+ return -1;
+ }
+ if (getStabilityManager().isEntryReorderingAllowed(entry)) {
+ // let the stability manager constrain or allow reordering
+ return -1;
+ }
+ return entry.getPreviousAttachState().getStableIndex();
}
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
deleted file mode 100644
index d73175310802..000000000000
--- a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import static androidx.lifecycle.Lifecycle.State.DESTROYED;
-import static androidx.lifecycle.Lifecycle.State.RESUMED;
-
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-
-/**
- * Tools for generating lifecycle from sysui objects.
- */
-public class SysuiLifecycle {
-
- private SysuiLifecycle() {
- }
-
- /**
- * Get a lifecycle that will be put into the resumed state when the view is attached
- * and goes to the destroyed state when the view is detached.
- */
- public static LifecycleOwner viewAttachLifecycle(View v) {
- return new ViewLifecycle(v);
- }
-
- private static class ViewLifecycle implements LifecycleOwner, OnAttachStateChangeListener {
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
-
- ViewLifecycle(View v) {
- v.addOnAttachStateChangeListener(this);
- if (v.isAttachedToWindow()) {
- mLifecycle.markState(RESUMED);
- }
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- mLifecycle.markState(RESUMED);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mLifecycle.markState(DESTROYED);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
index 328ad39cddd5..58d906907488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
@@ -41,11 +41,13 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
- @JvmField @Rule
+ @JvmField
+ @Rule
var mockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var callback: AuthBiometricView.Callback
+
@Mock
private lateinit var panelController: AuthPanelController
@@ -67,6 +69,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
fun fingerprintSuccessDoesNotRequireExplicitConfirmation() {
biometricView.onDialogAnimatedIn()
biometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT)
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -86,6 +89,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
// icon acts as confirm button
biometricView.mIconView.performClick()
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -102,6 +106,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR)
biometricView.onError(TYPE_FINGERPRINT, "that's a nope")
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
index 687cb517b2f4..bce98cf116d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
@@ -42,20 +42,23 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
class AuthBiometricFingerprintViewTest : SysuiTestCase() {
- @JvmField @Rule
+ @JvmField
+ @Rule
val mockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var callback: AuthBiometricView.Callback
+
@Mock
private lateinit var panelController: AuthPanelController
private lateinit var biometricView: AuthBiometricView
private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView {
- val view = R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView(
+ val view: AuthBiometricFingerprintView =
+ R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView(
mContext, callback, panelController, allowDeviceCredential = allowDeviceCredential
- ) as AuthBiometricFingerprintView
+ )
waitForIdleSync()
return view
}
@@ -73,6 +76,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -83,6 +87,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
biometricView.setRequireConfirmation(true)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
// TODO: this should be tested in the subclasses
@@ -104,6 +109,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testPositiveButton_sendsActionAuthenticated() {
biometricView.mConfirmButton.performClick()
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
@@ -114,6 +120,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
biometricView.onDialogAnimatedIn()
biometricView.mNegativeButton.performClick()
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE)
@@ -126,6 +133,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
biometricView.mCancelButton.performClick()
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED)
@@ -134,6 +142,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testTryAgainButton_sendsActionTryAgain() {
biometricView.mTryAgainButton.performClick()
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN)
@@ -144,6 +153,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testOnErrorSendsActionError() {
biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError")
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR))
@@ -156,6 +166,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val message = "another error"
biometricView.onError(BiometricAuthenticator.TYPE_FACE, message)
+ TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticating).isFalse()
@@ -178,6 +189,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val view = View(mContext)
biometricView.setBackgroundView(view)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ waitForIdleSync()
view.performClick()
verify(callback, never())
@@ -225,14 +237,14 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
biometricView.onSaveState(state)
assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY))
- .isEqualTo(View.GONE)
+ .isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE))
- .isEqualTo(AuthBiometricView.STATE_ERROR)
+ .isEqualTo(AuthBiometricView.STATE_ERROR)
assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue()
assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage)
assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING))
- .isEqualTo(failureMessage)
+ .isEqualTo(failureMessage)
// TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 09976e0e6192..571dd3d1faf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -106,7 +106,7 @@ public class ComplicationTypesUpdaterTest extends SysuiTestCase {
private ContentObserver captureSettingsObserver() {
verify(mSecureSettings).registerContentObserverForUser(
- eq(Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS),
+ eq(Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED),
mSettingsObserverCaptor.capture(), eq(UserHandle.myUserId()));
return mSettingsObserverCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 21c018a0419d..6e89bb90e558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -48,6 +48,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.logging.KeyguardViewMediatorLogger;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -106,6 +107,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+ private @Mock KeyguardViewMediatorLogger mLogger;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -262,7 +264,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mInteractionJankMonitor,
mDreamOverlayStateController,
mNotificationShadeWindowControllerLazy,
- () -> mActivityLaunchAnimator);
+ () -> mActivityLaunchAnimator,
+ mLogger);
mViewMediator.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 56aff3c2fc8b..7b12eb75e841 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -41,6 +41,18 @@ class LogBufferTest : SysuiTestCase() {
}
@Test
+ fun log_shouldSaveLogToBufferWithException() {
+ val exception = createTestException("Some exception test message", "SomeExceptionTestClass")
+ buffer.log("Test", LogLevel.INFO, "Some test message", exception)
+
+ val dumpedString = dumpBuffer()
+
+ assertThat(dumpedString).contains("Some test message")
+ assertThat(dumpedString).contains("Some exception test message")
+ assertThat(dumpedString).contains("SomeExceptionTestClass")
+ }
+
+ @Test
fun log_shouldRotateIfLogBufferIsFull() {
buffer.log("Test", LogLevel.INFO, "This should be rotated")
buffer.log("Test", LogLevel.INFO, "New test message")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
new file mode 100644
index 000000000000..83e56daf1fbc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.app.Application
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources.Strings.SystemUi.SCREENSHOT_BLOCKED_BY_ADMIN
+import android.app.admin.DevicePolicyResourcesManager
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.HARDWARE
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.ScreenshotHelper
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
+
+private const val USER_ID = 1
+private const val TASK_ID = 1
+
+@RunWith(AndroidTestingRunner::class)
+class TakeScreenshotServiceTest : SysuiTestCase() {
+
+ private val application = mock<Application>()
+ private val controller = mock<ScreenshotController>()
+ private val userManager = mock<UserManager>()
+ private val requestProcessor = mock<RequestProcessor>()
+ private val devicePolicyManager = mock<DevicePolicyManager>()
+ private val devicePolicyResourcesManager = mock<DevicePolicyResourcesManager>()
+ private val notificationsController = mock<ScreenshotNotificationsController>()
+ private val callback = mock<RequestCallback>()
+
+ private val eventLogger = UiEventLoggerFake()
+ private val flags = FakeFeatureFlags()
+ private val topComponent = ComponentName(mContext, TakeScreenshotServiceTest::class.java)
+
+ private val service = TakeScreenshotService(
+ controller, userManager, devicePolicyManager, eventLogger,
+ notificationsController, mContext, Runnable::run, flags, requestProcessor)
+
+ @Before
+ fun setUp() {
+ whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
+ whenever(devicePolicyManager.getScreenCaptureDisabled(
+ /* admin component (null: any admin) */ isNull(), eq(UserHandle.USER_ALL)))
+ .thenReturn(false)
+ whenever(userManager.isUserUnlocked).thenReturn(true)
+
+ flags.set(SCREENSHOT_REQUEST_PROCESSOR, false)
+
+ service.attach(
+ mContext,
+ /* thread = */ null,
+ /* className = */ null,
+ /* token = */ null,
+ application,
+ /* activityManager = */ null)
+ }
+
+ @Test
+ fun testServiceLifecycle() {
+ service.onCreate()
+ service.onBind(null /* unused: Intent */)
+
+ service.onUnbind(null /* unused: Intent */)
+ verify(controller).removeWindow()
+
+ service.onDestroy()
+ verify(controller).onDestroy()
+ }
+
+ @Test
+ fun takeScreenshotFullscreen() {
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).takeScreenshotFullscreen(
+ eq(topComponent),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotPartial() {
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_SELECTED_REGION,
+ SCREENSHOT_KEY_CHORD,
+ /* topComponent = */ null)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).takeScreenshotPartial(
+ /* topComponent = */ isNull(),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
+ assertEquals("Expected empty package name in UiEvent", "", eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotProvidedImage() {
+ val bounds = Rect(50, 50, 150, 150)
+ val bitmap = makeHardwareBitmap(100, 100)
+ val bitmapBundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+ val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW,
+ bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).handleImageAsScreenshot(
+ argThat { b -> b.equalsHardwareBitmap(bitmap) },
+ eq(bounds),
+ eq(Insets.NONE), eq(TASK_ID), eq(USER_ID), eq(topComponent),
+ /* onSavedListener = */ any(), /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED_* UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_OVERVIEW.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotFullscreen_userLocked() {
+ whenever(userManager.isUserUnlocked).thenReturn(false)
+
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(notificationsController).notifyScreenshotError(anyInt())
+ verify(callback).reportError()
+ verifyZeroInteractions(controller)
+ }
+
+ @Test
+ fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
+ whenever(devicePolicyManager.getScreenCaptureDisabled(
+ isNull(), eq(UserHandle.USER_ALL))
+ ).thenReturn(true)
+
+ whenever(devicePolicyResourcesManager.getString(
+ eq(SCREENSHOT_BLOCKED_BY_ADMIN),
+ /* Supplier<String> */ any(),
+ )).thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
+
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ // error shown: Toast.makeText(...).show(), untestable
+ verify(callback).reportError()
+ verifyZeroInteractions(controller)
+ }
+}
+
+private fun Bitmap.equalsHardwareBitmap(other: Bitmap): Boolean {
+ return config == HARDWARE &&
+ other.config == HARDWARE &&
+ hardwareBuffer == other.hardwareBuffer &&
+ colorSpace == other.colorSpace
+}
+
+/** A hardware Bitmap is mandated by use of ScreenshotHelper.HardwareBitmapBundler */
+private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
+ val buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 32fa6e45bc37..7d28871e340c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -905,7 +905,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
- assertThat(isKeyguardStatusViewCentered()).isTrue();
+ assertKeyguardStatusViewCentered();
}
@Test
@@ -916,7 +916,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
- assertThat(isKeyguardStatusViewCentered()).isFalse();
+ assertKeyguardStatusViewNotCentered();
}
@Test
@@ -927,19 +927,19 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
- assertThat(isKeyguardStatusViewCentered()).isFalse();
+ assertKeyguardStatusViewNotCentered();
}
@Test
- public void keyguardStatusView_splitShade_pulsing_isCentered() {
+ public void keyguardStatusView_splitShade_pulsing_isNotCentered() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
- assertThat(isKeyguardStatusViewCentered()).isFalse();
+ assertKeyguardStatusViewNotCentered();
}
@Test
@@ -949,9 +949,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
- setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
- assertThat(isKeyguardStatusViewCentered()).isFalse();
+ assertKeyguardStatusViewNotCentered();
}
@Test
@@ -964,7 +964,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mStatusBarStateController.setState(KEYGUARD);
setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
- assertThat(isKeyguardStatusViewCentered()).isFalse();
+ assertKeyguardStatusViewCentered();
}
@Test
@@ -1209,7 +1209,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void testSwitchesToBigClockInSplitShadeOnAod() {
+ public void clockSize_mediaShowing_inSplitShade_onAod_isLarge() {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
@@ -1217,10 +1218,25 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
clearInvocations(mKeyguardStatusViewController);
mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
+
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate= */ true);
}
@Test
+ public void clockSize_mediaShowing_inSplitShade_screenOff_notAod_isSmall() {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ clearInvocations(mKeyguardStatusViewController);
+
+ mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
+
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate= */ true);
+ }
+
+ @Test
public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
mStatusBarStateController.setState(KEYGUARD);
@@ -1547,9 +1563,15 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
);
}
- private boolean isKeyguardStatusViewCentered() {
+ private void assertKeyguardStatusViewCentered() {
+ mNotificationPanelViewController.updateResources();
+ assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf(
+ ConstraintSet.PARENT_ID, ConstraintSet.UNSET);
+ }
+
+ private void assertKeyguardStatusViewNotCentered() {
mNotificationPanelViewController.updateResources();
- return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
- == ConstraintSet.PARENT_ID;
+ assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo(
+ R.id.qs_edge_guideline);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 214ba16dfc44..64d025628754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -98,7 +98,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
@Test
fun testPrepareDialogForApp_onlyDefaultChannel() {
- group.channels = listOf(channelDefault)
+ group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
setOf(channelDefault), appIcon, clickListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
deleted file mode 100644
index 4f509eaaadde..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
-import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
-import static androidx.lifecycle.Lifecycle.Event.ON_START;
-import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-
-import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.view.View;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SysuiLifecycleTest extends SysuiTestCase {
-
- private View mView;
-
- @Before
- public void setUp() {
- mView = new View(mContext);
- }
-
- @After
- public void tearDown() {
- if (mView.isAttachedToWindow()) {
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- }
- }
-
- @Test
- public void testAttach() {
- LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- lifecycle.getLifecycle().addObserver(observer);
-
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_START));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME));
- }
-
- @Test
- public void testDetach() {
- LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- lifecycle.getLifecycle().addObserver(observer);
-
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY));
- }
-
- @Test
- public void testStateBeforeAttach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be INITIAZED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(
- Lifecycle.State.INITIALIZED);
- }
-
- @Test
- public void testStateAfterAttach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should be RESUMED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateAfterDetach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is detached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should be DESTROYED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.DESTROYED);
- }
-
- @Test
- public void testStateAfterReattach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is re-attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should still be DESTROYED, err RESUMED?
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateWhenViewAlreadyAttached() {
- // GIVEN that a view is already attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be RESUMED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateWhenViewAlreadyDetached() {
- // GIVEN that a view is already detached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be INITIALIZED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(
- Lifecycle.State.INITIALIZED);
- }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index b34482f0964f..3324c526ecc2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -354,16 +354,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags |=
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags &=
+ ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
}
}
if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
+ }
+
+ if (mAccessibilityServiceInfo.isAccessibilityTool()) {
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
+ } else {
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
}
mRequestTouchExplorationMode = (info.flags
@@ -1522,9 +1530,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
+ final boolean includeNotImportantViews = (mFetchFlags
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
&& !event.isImportantForAccessibility()
- && (mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
+ && !includeNotImportantViews) {
+ return false;
+ }
+
+ if (event.isAccessibilityDataPrivate()
+ && (mFetchFlags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) == 0) {
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6eabc981e9fe..6a6d2bb44d48 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3693,6 +3693,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ info.setAccessibilityTool(true);
final AccessibilityUserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index fa043f8e02af..448d70c6b9a4 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.companion;
-import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
@@ -88,7 +87,6 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
-import android.util.Base64;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -609,10 +607,8 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) {
- if (DEBUG) {
- Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName
- + ", macAddress=" + deviceMacAddress);
- }
+ Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName
+ + ", macAddress=" + deviceMacAddress);
requireNonNull(deviceMacAddress);
requireNonNull(packageName);
@@ -624,7 +620,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void disassociate(int associationId) {
- if (DEBUG) Log.i(TAG, "disassociate() associationId=" + associationId);
+ Log.i(TAG, "disassociate() associationId=" + associationId);
final AssociationInfo association =
getAssociationWithCallerChecks(associationId);
@@ -756,6 +752,11 @@ public class CompanionDeviceManagerService extends SystemService {
mDevicePresenceMonitor.onSelfManagedDeviceDisconnected(associationId);
}
+ @Override
+ public boolean isCompanionApplicationBound(String packageName, int userId) {
+ return mCompanionAppController.isCompanionApplicationBound(userId, packageName);
+ }
+
private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
boolean active) throws RemoteException {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index b8815454cadb..b33d84c0f275 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.system.OsConstants.O_RDONLY;
import android.content.BroadcastReceiver;
@@ -26,8 +27,10 @@ import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FileUtils;
import android.os.MessageQueue.OnFileDescriptorEventListener;
+import android.os.ParcelFileDescriptor;
import android.os.RecoverySystem;
import android.os.SystemProperties;
+import android.os.TombstoneWithHeadersProto;
import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
@@ -38,6 +41,7 @@ import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -54,6 +58,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermissions;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
@@ -78,6 +84,11 @@ public class BootReceiver extends BroadcastReceiver {
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
private static final String TAG_TOMBSTONE_PROTO = "SYSTEM_TOMBSTONE_PROTO";
+ private static final String TAG_TOMBSTONE_PROTO_WITH_HEADERS =
+ "SYSTEM_TOMBSTONE_PROTO_WITH_HEADERS";
+
+ // Directory to store temporary tombstones.
+ private static final File TOMBSTONE_TMP_DIR = new File("/data/tombstones");
// The pre-froyo package and class of the system updater, which
// ran in the system process. We need to remove its packages here
@@ -329,7 +340,44 @@ public class BootReceiver extends BroadcastReceiver {
try {
if (proto) {
if (recordFileTimestamp(tombstone, timestamps)) {
- db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ // We need to attach the count indicating the number of dropped dropbox entries
+ // due to rate limiting. Do this by enclosing the proto tombsstone in a
+ // container proto that has the dropped entry count and the proto tombstone as
+ // bytes (to avoid the complexity of reading and writing nested protos).
+
+ // Read the proto tombstone file as bytes.
+ final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+
+ final File tombstoneProtoWithHeaders = File.createTempFile(
+ tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
+ Files.setPosixFilePermissions(
+ tombstoneProtoWithHeaders.toPath(),
+ PosixFilePermissions.fromString("rw-rw----"));
+
+ // Write the new proto container proto with headers.
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(tombstoneProtoWithHeaders, MODE_READ_WRITE);
+
+ ProtoOutputStream protoStream = new ProtoOutputStream(
+ pfd.getFileDescriptor());
+ protoStream.write(TombstoneWithHeadersProto.TOMBSTONE, tombstoneBytes);
+ protoStream.write(
+ TombstoneWithHeadersProto.DROPPED_COUNT,
+ rateLimitResult.droppedCountSinceRateLimitActivated());
+ protoStream.flush();
+
+ // Add the proto to dropbox.
+ db.addFile(TAG_TOMBSTONE_PROTO_WITH_HEADERS, tombstoneProtoWithHeaders, 0);
+ } catch (FileNotFoundException ex) {
+ Slog.e(TAG, "failed to open for write: " + tombstoneProtoWithHeaders, ex);
+ throw ex;
+ } finally {
+ // Remove the temporary file.
+ if (tombstoneProtoWithHeaders != null) {
+ tombstoneProtoWithHeaders.delete();
+ }
+ }
}
} else {
// Add the header indicating how many events have been dropped due to rate limiting.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb1d2d762776..f7833b0f36fd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -339,7 +339,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int[] mDataConnectionNetworkType;
- private ArrayList<List<CellInfo>> mCellInfo = null;
+ private ArrayList<List<CellInfo>> mCellInfo;
private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
@@ -725,7 +725,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
mCellIdentity[i] = null;
- mCellInfo.add(i, null);
+ mCellInfo.add(i, Collections.EMPTY_LIST);
mImsReasonInfo.add(i, null);
mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
@@ -802,7 +802,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallNetworkType = new int[numPhones];
mCallAttributes = new CallAttributes[numPhones];
mPreciseDataConnectionStates = new ArrayList<>();
- mCellInfo = new ArrayList<>();
+ mCellInfo = new ArrayList<>(numPhones);
mImsReasonInfo = new ArrayList<>();
mEmergencyNumberList = new HashMap<>();
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
@@ -832,7 +832,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
mCellIdentity[i] = null;
- mCellInfo.add(i, null);
+ mCellInfo.add(i, Collections.EMPTY_LIST);
mImsReasonInfo.add(i, null);
mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
@@ -1803,10 +1803,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyCellInfoForSubscriber()")) {
return;
}
+
if (VDBG) {
log("notifyCellInfoForSubscriber: subId=" + subId
+ " cellInfo=" + cellInfo);
}
+
+ if (cellInfo == null) {
+ loge("notifyCellInfoForSubscriber() received a null list");
+ cellInfo = Collections.EMPTY_LIST;
+ }
+
int phoneId = getPhoneIdFromSubId(subId);
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 734f45523618..a0c2ad57241b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2401,13 +2401,13 @@ public class ActivityManagerService extends IActivityManager.Stub
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
- mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"foreground", foreConstants, false);
- mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"background", backConstants, true);
- mBgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"offload_bg", offloadConstants, true);
- mFgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mFgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"offload_fg", foreConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -10626,8 +10626,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!onlyHistory && !onlyReceivers && dumpAll) {
pw.println();
for (BroadcastQueue queue : mBroadcastQueues) {
- pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]="
- + queue.mBroadcastsScheduled);
+ pw.println(" Queue " + queue.toString() + ": " + queue.describeState());
}
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
@@ -13082,17 +13081,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
boolean isPendingBroadcastProcessLocked(int pid) {
- return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
- || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
- || mBgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid)
- || mFgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid);
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ BroadcastRecord r = queue.getPendingBroadcastLocked();
+ if (r != null && r.curApp.getPid() == pid) {
+ return true;
+ }
+ }
+ return false;
}
boolean isPendingBroadcastProcessLocked(ProcessRecord app) {
- return mFgBroadcastQueue.isPendingBroadcastProcessLocked(app)
- || mBgBroadcastQueue.isPendingBroadcastProcessLocked(app)
- || mBgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(app)
- || mFgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(app);
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ BroadcastRecord r = queue.getPendingBroadcastLocked();
+ if (r != null && r.curApp == app) {
+ return true;
+ }
+ }
+ return false;
}
void skipPendingBroadcastLocked(int pid) {
@@ -13114,7 +13119,6 @@ public class ActivityManagerService extends IActivityManager.Stub
void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
for (BroadcastQueue queue : mBroadcastQueues) {
queue.updateUidReadyForBootCompletedBroadcastLocked(uid);
- queue.scheduleBroadcastsLocked();
}
}
@@ -13364,8 +13368,7 @@ public class ActivityManagerService extends IActivityManager.Stub
receivers, null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
null /* filterExtrasForReceiver */);
- queue.enqueueParallelBroadcastLocked(r);
- queue.scheduleBroadcastsLocked();
+ queue.enqueueBroadcastLocked(r);
}
}
@@ -14243,13 +14246,7 @@ public class ActivityManagerService extends IActivityManager.Stub
sticky, false, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
- final boolean replaced = replacePending
- && (queue.replaceParallelBroadcastLocked(r) != null);
- // Note: We assume resultTo is null for non-ordered broadcasts.
- if (!replaced) {
- queue.enqueueParallelBroadcastLocked(r);
- queue.scheduleBroadcastsLocked();
- }
+ queue.enqueueBroadcastLocked(r);
registeredReceivers = null;
NR = 0;
}
@@ -14342,31 +14339,7 @@ public class ActivityManagerService extends IActivityManager.Stub
backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
-
- final BroadcastRecord oldRecord =
- replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
- if (oldRecord != null) {
- // Replaced, fire the result-to receiver.
- if (oldRecord.resultTo != null) {
- final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
- try {
- oldRecord.mIsReceiverAppRunning = true;
- oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
- oldRecord.intent,
- Activity.RESULT_CANCELED, null, null,
- false, false, oldRecord.userId, oldRecord.callingUid, callingUid,
- SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure ["
- + queue.mQueueName + "] sending broadcast result of "
- + intent, e);
-
- }
- }
- } else {
- queue.enqueueOrderedBroadcastLocked(r);
- queue.scheduleBroadcastsLocked();
- }
+ queue.enqueueBroadcastLocked(r);
} else {
// There was nobody interested in the broadcast, but we still want to record
// that it happened.
@@ -15186,7 +15159,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// It's not the current receiver, but it might be starting up to become one
for (BroadcastQueue queue : mBroadcastQueues) {
- final BroadcastRecord r = queue.mPendingBroadcast;
+ final BroadcastRecord r = queue.getPendingBroadcastLocked();
if (r != null && r.curApp == app) {
// found it; report which queue it's in
receivingQueues.add(queue);
@@ -15301,7 +15274,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
final boolean canGcNowLocked() {
for (BroadcastQueue q : mBroadcastQueues) {
- if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isIdle()) {
+ if (!q.isIdle()) {
return false;
}
}
@@ -17890,7 +17863,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.flush();
}
Slog.v(TAG, msg);
- queue.cancelDeferrals();
+ queue.flush();
idle = false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 5cb25d36d94b..36908ce8c7b3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -49,10 +49,12 @@ import android.app.BroadcastOptions;
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
+import android.app.IProcessObserver;
import android.app.IStopUserCallback;
import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
+import android.app.ProcessStateEnum;
import android.app.ProfilerInfo;
import android.app.RemoteServiceException.CrashedByAdbException;
import android.app.UserSwitchObserver;
@@ -359,6 +361,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runSetBgRestrictionLevel(pw);
case "get-bg-restriction-level":
return runGetBgRestrictionLevel(pw);
+ case "observe-foreground-process":
+ return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
default:
return handleDefaultCommands(cmd);
}
@@ -3230,6 +3234,82 @@ final class ActivityManagerShellCommand extends ShellCommand {
return -1;
}
+ private int runGetCurrentForegroundProcess(PrintWriter pw,
+ IActivityManager iam, IActivityTaskManager iatm)
+ throws RemoteException {
+
+ ProcessObserver observer = new ProcessObserver(pw, iam, iatm, mInternal);
+ iam.registerProcessObserver(observer);
+
+ final InputStream mInput = getRawInputStream();
+ InputStreamReader converter = new InputStreamReader(mInput);
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+ try {
+ while ((line = in.readLine()) != null) {
+ boolean addNewline = true;
+ if (line.length() <= 0) {
+ addNewline = false;
+ } else if ("q".equals(line) || "quit".equals(line)) {
+ break;
+ } else {
+ pw.println("Invalid command: " + line);
+ }
+ if (addNewline) {
+ pw.println("");
+ }
+ pw.flush();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ pw.flush();
+ } finally {
+ iam.unregisterProcessObserver(observer);
+ }
+ return 0;
+ }
+
+ static final class ProcessObserver extends IProcessObserver.Stub {
+
+ private PrintWriter mPw;
+ private IActivityManager mIam;
+ private IActivityTaskManager mIatm;
+ private ActivityManagerService mInternal;
+
+ ProcessObserver(PrintWriter mPw, IActivityManager mIam,
+ IActivityTaskManager mIatm, ActivityManagerService ams) {
+ this.mPw = mPw;
+ this.mIam = mIam;
+ this.mIatm = mIatm;
+ this.mInternal = ams;
+ }
+
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ if (foregroundActivities) {
+ try {
+ int prcState = mIam.getUidProcessState(uid, "android");
+ int topPid = mInternal.getTopApp().getPid();
+ if (prcState == ProcessStateEnum.TOP && topPid == pid) {
+ mPw.println("New foreground process: " + pid);
+ }
+ mPw.flush();
+ } catch (RemoteException e) {
+ mPw.println("Error occurred in binder call");
+ mPw.flush();
+ }
+ }
+ }
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ }
+ }
+
private int runSetMemoryFactor(PrintWriter pw) throws RemoteException {
final String levelArg = getNextArgRequired();
@MemFactor int level = ADJ_MEM_FACTOR_NOTHING;
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 49477ad75302..e9a36e05c6c1 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -162,7 +162,7 @@ public class BroadcastDispatcher {
}
private final Object mLock;
- private final BroadcastQueue mQueue;
+ private final BroadcastQueueImpl mQueue;
private final BroadcastConstants mConstants;
private final Handler mHandler;
private AlarmManagerInternal mAlarm;
@@ -489,7 +489,7 @@ public class BroadcastDispatcher {
/**
* Constructed & sharing a lock with its associated BroadcastQueue instance
*/
- public BroadcastDispatcher(BroadcastQueue queue, BroadcastConstants constants,
+ public BroadcastDispatcher(BroadcastQueueImpl queue, BroadcastConstants constants,
Handler handler, Object lock) {
mQueue = queue;
mConstants = constants;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 31d9f96d46e4..d0946bec730a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,236 +16,40 @@
package com.android.server.am;
-import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
-import static android.text.TextUtils.formatSimple;
-
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
-import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
-import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
-import android.app.RemoteServiceException.CannotDeliverBroadcastException;
-import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager.ReasonCode;
-import android.os.PowerExemptionManager.TempAllowListType;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.permission.IPermissionManager;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Slog;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.os.TimeoutRecord;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
import java.util.Set;
/**
- * BROADCASTS
- *
- * We keep three broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts, and one to
- * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
+ * Queue of broadcast intents and associated bookkeeping.
*/
-public final class BroadcastQueue {
- private static final String TAG = "BroadcastQueue";
- private static final String TAG_MU = TAG + POSTFIX_MU;
- private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
-
- static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
- static final int MAX_BROADCAST_SUMMARY_HISTORY
- = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
+public abstract class BroadcastQueue {
+ public static final String TAG = "BroadcastQueue";
final ActivityManagerService mService;
-
- /**
- * Behavioral parameters such as timeouts and deferral policy, tracking Settings
- * for runtime configurability
- */
+ final Handler mHandler;
final BroadcastConstants mConstants;
-
- /**
- * Recognizable moniker for this queue
- */
+ final BroadcastSkipPolicy mSkipPolicy;
final String mQueueName;
- /**
- * If true, we can delay broadcasts while waiting services to finish in the previous
- * receiver's process.
- */
- final boolean mDelayBehindServices;
-
- /**
- * Lists of all active broadcasts that are to be executed immediately
- * (without waiting for another broadcast to finish). Currently this only
- * contains broadcasts to registered receivers, to avoid spinning up
- * a bunch of processes to execute IntentReceiver components. Background-
- * and foreground-priority broadcasts are queued separately.
- */
- final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
-
- /**
- * Tracking of the ordered broadcast queue, including deferral policy and alarm
- * prioritization.
- */
- final BroadcastDispatcher mDispatcher;
-
- /**
- * Refcounting for completion callbacks of split/deferred broadcasts. The key
- * is an opaque integer token assigned lazily when a broadcast is first split
- * into multiple BroadcastRecord objects.
- */
- final SparseIntArray mSplitRefcounts = new SparseIntArray();
- private int mNextToken = 0;
-
- /**
- * Historical data of past broadcasts, for debugging. This is a ring buffer
- * whose last element is at mHistoryNext.
- */
- final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
- int mHistoryNext = 0;
-
- /**
- * Summary of historical data of past broadcasts, for debugging. This is a
- * ring buffer whose last element is at mSummaryHistoryNext.
- */
- final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
- int mSummaryHistoryNext = 0;
-
- /**
- * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
- * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
- * clock time, not elapsed.
- */
- final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
-
- /**
- * Set when we current have a BROADCAST_INTENT_MSG in flight.
- */
- boolean mBroadcastsScheduled = false;
-
- /**
- * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
- */
- boolean mPendingBroadcastTimeoutMessage;
-
- /**
- * Intent broadcasts that we have tried to start, but are
- * waiting for the application's process to be created. We only
- * need one per scheduling class (instead of a list) because we always
- * process broadcasts one at a time, so no others can be started while
- * waiting for this one.
- */
- BroadcastRecord mPendingBroadcast = null;
-
- /**
- * The receiver index that is pending, to restart the broadcast if needed.
- */
- int mPendingBroadcastRecvIndex;
-
- static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
- static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
-
- // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
- boolean mLogLatencyMetrics = true;
-
- final BroadcastHandler mHandler;
-
- private final class BroadcastHandler extends Handler {
- public BroadcastHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case BROADCAST_INTENT_MSG: {
- if (DEBUG_BROADCAST) Slog.v(
- TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
- + mQueueName + "]");
- processNextBroadcast(true);
- } break;
- case BROADCAST_TIMEOUT_MSG: {
- synchronized (mService) {
- broadcastTimeoutLocked(true);
- }
- } break;
- }
- }
- }
-
BroadcastQueue(ActivityManagerService service, Handler handler,
- String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
+ String name, BroadcastConstants constants) {
mService = service;
- mHandler = new BroadcastHandler(handler.getLooper());
+ mHandler = handler;
mQueueName = name;
- mDelayBehindServices = allowDelayBehindServices;
-
mConstants = constants;
- mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
+ mSkipPolicy = new BroadcastSkipPolicy(service);
}
void start(ContentResolver resolver) {
- mDispatcher.start();
mConstants.startObserving(mHandler, resolver);
}
@@ -254,2322 +58,78 @@ public final class BroadcastQueue {
return mQueueName;
}
- public boolean isPendingBroadcastProcessLocked(int pid) {
- return mPendingBroadcast != null && mPendingBroadcast.curApp.getPid() == pid;
- }
-
- boolean isPendingBroadcastProcessLocked(ProcessRecord app) {
- return mPendingBroadcast != null && mPendingBroadcast.curApp == app;
- }
-
- public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
- r.enqueueClockTime = System.currentTimeMillis();
- r.enqueueTime = SystemClock.uptimeMillis();
- r.enqueueRealTime = SystemClock.elapsedRealtime();
- mParallelBroadcasts.add(r);
- enqueueBroadcastHelper(r);
- }
-
- public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- r.enqueueClockTime = System.currentTimeMillis();
- r.enqueueTime = SystemClock.uptimeMillis();
- r.enqueueRealTime = SystemClock.elapsedRealtime();
- mDispatcher.enqueueOrderedBroadcastLocked(r);
- enqueueBroadcastHelper(r);
- }
+ public abstract boolean isDelayBehindServices();
- /**
- * Don't call this method directly; call enqueueParallelBroadcastLocked or
- * enqueueOrderedBroadcastLocked.
- */
- private void enqueueBroadcastHelper(BroadcastRecord r) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- }
- }
+ public abstract BroadcastRecord getPendingBroadcastLocked();
- /**
- * Find the same intent from queued parallel broadcast, replace with a new one and return
- * the old one.
- */
- public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
- return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
- }
+ public abstract BroadcastRecord getActiveBroadcastLocked();
/**
- * Find the same intent from queued ordered broadcast, replace with a new one and return
- * the old one.
+ * Enqueue the given broadcast to be eventually dispatched.
+ * <p>
+ * Callers must populate {@link BroadcastRecord#receivers} with the relevant
+ * targets before invoking this method.
+ * <p>
+ * When {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} is set, this method
+ * internally handles replacement of any matching broadcasts.
*/
- public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
- return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
- }
+ public abstract void enqueueBroadcastLocked(BroadcastRecord r);
- private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
- BroadcastRecord r, String typeForLogging) {
- final Intent intent = r.intent;
- for (int i = queue.size() - 1; i > 0; i--) {
- final BroadcastRecord old = queue.get(i);
- if (old.userId == r.userId && intent.filterEquals(old.intent)) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG_BROADCAST, "***** DROPPING "
- + typeForLogging + " [" + mQueueName + "]: " + intent);
- }
- queue.set(i, r);
- return old;
- }
- }
- return null;
- }
+ public abstract void updateUidReadyForBootCompletedBroadcastLocked(int uid);
- private final void processCurBroadcastLocked(BroadcastRecord r,
- ProcessRecord app) throws RemoteException {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + " for app " + app);
- final IApplicationThread thread = app.getThread();
- if (thread == null) {
- throw new RemoteException();
- }
- if (app.isInFullBackup()) {
- skipReceiverLocked(r);
- return;
- }
+ public abstract boolean sendPendingBroadcastsLocked(ProcessRecord app);
- r.receiver = thread.asBinder();
- r.curApp = app;
- final ProcessReceiverRecord prr = app.mReceivers;
- prr.addCurReceiver(r);
- app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- // Don't bump its LRU position if it's in the background restricted.
- if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
- < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
- mService.updateLruProcessLocked(app, false, null);
- }
- // Make sure the oom adj score is updated before delivering the broadcast.
- // Force an update, even if there are other pending requests, overall it still saves time,
- // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
- mService.enqueueOomAdjTargetLocked(app);
- mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+ public abstract void skipPendingBroadcastLocked(int pid);
- // Tell the application to launch this receiver.
- maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
- r.intent.setComponent(r.curComponent);
+ public abstract void skipCurrentReceiverLocked(ProcessRecord app);
- boolean started = false;
- try {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Delivering to component " + r.curComponent
- + ": " + r);
- mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
- PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
- thread.scheduleReceiver(prepareReceiverIntent(r.intent, r.curFilteredExtras),
- r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
- r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
- app.mState.getReportedProcState());
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + " DELIVERED for app " + app);
- started = true;
- } finally {
- if (!started) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + ": NOT STARTED!");
- r.receiver = null;
- r.curApp = null;
- prr.removeCurReceiver(r);
- }
- }
-
- // if something bad happens here, launch the app and try again
- if (app.isKilled()) {
- throw new RemoteException("app gets killed during broadcasting");
- }
- }
+ public abstract BroadcastRecord getMatchingOrderedReceiver(IBinder receiver);
/**
- * Called by ActivityManagerService to notify that the uid has process started, if there is any
- * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
- * @param uid
+ * Signal delivered back from a {@link BroadcastReceiver} to indicate that
+ * it's finished processing the current broadcast being dispatched to it.
+ * <p>
+ * If this signal isn't delivered back in a timely fashion, we assume the
+ * receiver has somehow wedged and we trigger an ANR.
*/
- public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
- mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
- }
-
- public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
- boolean didSomething = false;
- final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
- if (br.curApp != app) {
- Slog.e(TAG, "App mismatch when sending pending broadcast to "
- + app.processName + ", intended target is " + br.curApp.processName);
- return false;
- }
- try {
- mPendingBroadcast = null;
- br.mIsReceiverAppRunning = false;
- processCurBroadcastLocked(br, app);
- didSomething = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting receiver "
- + br.curComponent.flattenToShortString(), e);
- logBroadcastReceiverDiscardLocked(br);
- finishReceiverLocked(br, br.resultCode, br.resultData,
- br.resultExtras, br.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- br.state = BroadcastRecord.IDLE;
- throw new RuntimeException(e.getMessage());
- }
- }
- return didSomething;
- }
-
- public void skipPendingBroadcastLocked(int pid) {
- final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.getPid() == pid) {
- br.state = BroadcastRecord.IDLE;
- br.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- scheduleBroadcastsLocked();
- }
- }
+ public abstract boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices);
- // Skip the current receiver, if any, that is in flight to the given process
- public void skipCurrentReceiverLocked(ProcessRecord app) {
- BroadcastRecord r = null;
- final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
- if (curActive != null && curActive.curApp == app) {
- // confirmed: the current active broadcast is to the given app
- r = curActive;
- }
+ public abstract void backgroundServicesFinishedLocked(int userId);
- // If the current active broadcast isn't this BUT we're waiting for
- // mPendingBroadcast to spin up the target app, that's what we use.
- if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "[" + mQueueName + "] skip & discard pending app " + r);
- r = mPendingBroadcast;
- }
-
- if (r != null) {
- skipReceiverLocked(r);
- }
- }
-
- private void skipReceiverLocked(BroadcastRecord r) {
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- }
-
- public void scheduleBroadcastsLocked() {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
- + mQueueName + "]: current="
- + mBroadcastsScheduled);
-
- if (mBroadcastsScheduled) {
- return;
- }
- mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
- mBroadcastsScheduled = true;
- }
-
- public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
- BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
- if (br != null && br.receiver == receiver) {
- return br;
- }
- return null;
- }
-
- // > 0 only, no worry about "eventual" recycling
- private int nextSplitTokenLocked() {
- int next = mNextToken + 1;
- if (next <= 0) {
- next = 1;
- }
- mNextToken = next;
- return next;
- }
-
- private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
- // the receiver had run for less than allowed bg activity start timeout,
- // so allow the process to still start activities from bg for some more time
- String msgToken = (app.toShortString() + r.toString()).intern();
- // first, if there exists a past scheduled request to remove this token, drop
- // that request - we don't want the token to be swept from under our feet...
- mHandler.removeCallbacksAndMessages(msgToken);
- // ...then schedule the removal of the token after the extended timeout
- mHandler.postAtTime(() -> {
- synchronized (mService) {
- app.removeAllowBackgroundActivityStartsToken(r);
- }
- }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
- }
-
- public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
- String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
- final int state = r.state;
- final ActivityInfo receiver = r.curReceiver;
- final long finishTime = SystemClock.uptimeMillis();
- final long elapsed = finishTime - r.receiverTime;
- r.state = BroadcastRecord.IDLE;
- final int curIndex = r.nextReceiver - 1;
- if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
- final Object curReceiver = r.receivers.get(curIndex);
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
- r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
- ActivityManagerService.getShortAction(r.intent.getAction()),
- curReceiver instanceof BroadcastFilter
- ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
- : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- r.mIsReceiverAppRunning
- ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
- : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
- r.dispatchTime - r.enqueueTime,
- r.receiverTime - r.dispatchTime,
- finishTime - r.receiverTime);
- }
- if (state == BroadcastRecord.IDLE) {
- Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
- }
- if (r.allowBackgroundActivityStarts && r.curApp != null) {
- if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
- // if the receiver has run for more than allowed bg activity start timeout,
- // just remove the token for this process now and we're done
- r.curApp.removeAllowBackgroundActivityStartsToken(r);
- } else {
- // It gets more time; post the removal to happen at the appropriate moment
- postActivityStartTokenRemoval(r.curApp, r);
- }
- }
- // If we're abandoning this broadcast before any receivers were actually spun up,
- // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
- if (r.nextReceiver > 0) {
- r.duration[r.nextReceiver - 1] = elapsed;
- }
-
- // if this receiver was slow, impose deferral policy on the app. This will kick in
- // when processNextBroadcastLocked() next finds this uid as a receiver identity.
- if (!r.timeoutExempt) {
- // r.curApp can be null if finish has raced with process death - benign
- // edge case, and we just ignore it because we're already cleaning up
- // as expected.
- if (r.curApp != null
- && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
- // Core system packages are exempt from deferral policy
- if (!UserHandle.isCore(r.curApp.uid)) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
- + " was slow: " + receiver + " br=" + r);
- }
- mDispatcher.startDeferring(r.curApp.uid);
- } else {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
- + " receiver was slow but not deferring: "
- + receiver + " br=" + r);
- }
- }
- }
- } else {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
- + " is exempt from deferral policy");
- }
- }
-
- r.receiver = null;
- r.intent.setComponent(null);
- if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
- r.curApp.mReceivers.removeCurReceiver(r);
- mService.enqueueOomAdjTargetLocked(r.curApp);
- }
- if (r.curFilter != null) {
- r.curFilter.receiverList.curBroadcast = null;
- }
- r.curFilter = null;
- r.curReceiver = null;
- r.curApp = null;
- r.curFilteredExtras = null;
- mPendingBroadcast = null;
-
- r.resultCode = resultCode;
- r.resultData = resultData;
- r.resultExtras = resultExtras;
- if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
- r.resultAbort = resultAbort;
- } else {
- r.resultAbort = false;
- }
-
- // If we want to wait behind services *AND* we're finishing the head/
- // active broadcast on its queue
- if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
- && r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
- ActivityInfo nextReceiver;
- if (r.nextReceiver < r.receivers.size()) {
- Object obj = r.receivers.get(r.nextReceiver);
- nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
- } else {
- nextReceiver = null;
- }
- // Don't do this if the next receive is in the same process as the current one.
- if (receiver == null || nextReceiver == null
- || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
- || !receiver.processName.equals(nextReceiver.processName)) {
- // In this case, we are ready to process the next receiver for the current broadcast,
- // but are on a queue that would like to wait for services to finish before moving
- // on. If there are background services currently starting, then we will go into a
- // special state where we hold off on continuing this broadcast until they are done.
- if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
- Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
- r.state = BroadcastRecord.WAITING_SERVICES;
- return false;
- }
- }
- }
-
- r.curComponent = null;
-
- // We will process the next receiver right now if this is finishing
- // an app receiver (which is always asynchronous) or after we have
- // come back from calling a receiver.
- return state == BroadcastRecord.APP_RECEIVE
- || state == BroadcastRecord.CALL_DONE_RECEIVE;
- }
-
- public void backgroundServicesFinishedLocked(int userId) {
- BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
- if (br != null) {
- if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
- Slog.i(TAG, "Resuming delayed broadcast");
- br.curComponent = null;
- br.state = BroadcastRecord.IDLE;
- processNextBroadcastLocked(false, false);
- }
- }
- }
-
- void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
- Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser,
- int receiverUid, int callingUid, long dispatchDelay,
- long receiveDelay) throws RemoteException {
- // Send the intent to the receiver asynchronously using one-way binder calls.
- if (app != null) {
- final IApplicationThread thread = app.getThread();
- if (thread != null) {
- // If we have an app thread, do the call through that so it is
- // correctly ordered with other one-way calls.
- try {
- thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser,
- app.mState.getReportedProcState());
- // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
- // DeadObjectException when the process isn't actually dead.
- //} catch (DeadObjectException ex) {
- // Failed to call into the process. It's dying so just let it die and move on.
- // throw ex;
- } catch (RemoteException ex) {
- // Failed to call into the process. It's either dying or wedged. Kill it gently.
- synchronized (mService) {
- Slog.w(TAG, "Can't deliver broadcast to " + app.processName
- + " (pid " + app.getPid() + "). Crashing it.");
- app.scheduleCrashLocked("can't deliver broadcast",
- CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
- }
- throw ex;
- }
- } else {
- // Application has died. Receiver doesn't exist.
- throw new RemoteException("app.thread must not be null");
- }
- } else {
- receiver.performReceive(intent, resultCode, data, extras, ordered,
- sticky, sendingUser);
- }
- if (!ordered) {
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
- receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
- callingUid == -1 ? Process.SYSTEM_UID : callingUid,
- ActivityManagerService.getShortAction(intent.getAction()),
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
- dispatchDelay, receiveDelay, 0 /* finish_delay */);
- }
- }
-
- private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
- BroadcastFilter filter, boolean ordered, int index) {
- boolean skip = false;
- if (r.options != null && !r.options.testRequireCompatChange(filter.owningUid)) {
- Slog.w(TAG, "Compat change filtered: broadcasting " + r.intent.toString()
- + " to uid " + filter.owningUid + " due to compat change "
- + r.options.getRequireCompatChangeId());
- skip = true;
- }
- if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
- filter.packageName, filter.owningUid)) {
- Slog.w(TAG, "Association not allowed: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
- + filter);
- skip = true;
- }
- if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, filter.receiverList.uid)) {
- Slog.w(TAG, "Firewall blocked: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
- + filter);
- skip = true;
- }
- // Check that the sender has permission to send to this receiver
- if (filter.requiredPermission != null) {
- int perm = mService.checkComponentPermission(filter.requiredPermission,
- r.callingPid, r.callingUid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " requires " + filter.requiredPermission
- + " due to registered receiver " + filter);
- skip = true;
- } else {
- final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
- if (opCode != AppOpsManager.OP_NONE
- && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
- r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- filter.requiredPermission)
- + " due to registered receiver " + filter);
- skip = true;
- }
- }
- }
-
- if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()
- || filter.receiverList.app.mErrorState.isCrashing())) {
- Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
- + " to " + filter.receiverList + ": process gone or crashing");
- skip = true;
- }
-
- // Ensure that broadcasts are only sent to other Instant Apps if they are marked as
- // visible to Instant Apps.
- final boolean visibleToInstantApps =
- (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
-
- if (!skip && !visibleToInstantApps && filter.instantApp
- && filter.receiverList.uid != r.callingUid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")"
- + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
- skip = true;
- }
-
- if (!skip && !filter.visibleToInstantApp && r.callerInstantApp
- && filter.receiverList.uid != r.callingUid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires receiver be visible to instant apps"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
-
- // Check that the receiver has the required permission(s) to receive this broadcast.
- if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- int perm = mService.checkComponentPermission(requiredPermission,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.getAppOpsManager().noteOpNoThrow(appOp,
- filter.receiverList.uid, filter.packageName, filter.featureId,
- "Broadcast delivered to registered receiver " + filter.receiverId)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- if (!skip && (r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
- int perm = mService.checkComponentPermission(null,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: security check failed when receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
- // Check that the receiver does *not* have any excluded permissions
- if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
- for (int i = 0; i < r.excludedPermissions.length; i++) {
- String excludedPermission = r.excludedPermissions[i];
- final int perm = mService.checkComponentPermission(excludedPermission,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
-
- int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
- if (appOp != AppOpsManager.OP_NONE) {
- // When there is an app op associated with the permission,
- // skip when both the permission and the app op are
- // granted.
- if ((perm == PackageManager.PERMISSION_GRANTED) && (
- mService.getAppOpsManager().checkOpNoThrow(appOp,
- filter.receiverList.uid,
- filter.packageName)
- == AppOpsManager.MODE_ALLOWED)) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes appop " + AppOpsManager.permissionToOp(
- excludedPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- } else {
- // When there is no app op associated with the permission,
- // skip when permission is granted.
- if (perm == PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes " + excludedPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- }
-
- // Check that the receiver does *not* belong to any of the excluded packages
- if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
- if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) {
- Slog.w(TAG, "Skipping delivery of excluded package "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes package " + filter.packageName
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
-
- // If the broadcast also requires an app op check that as well.
- if (!skip && r.appOp != AppOpsManager.OP_NONE
- && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
- filter.receiverList.uid, filter.packageName, filter.featureId,
- "Broadcast delivered to registered receiver " + filter.receiverId)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires appop " + AppOpsManager.opToName(r.appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
-
- // Ensure that broadcasts are only sent to other apps if they are explicitly marked as
- // exported, or are System level broadcasts
- if (!skip && !filter.exported && mService.checkComponentPermission(null, r.callingPid,
- r.callingUid, filter.receiverList.uid, filter.exported)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Exported Denial: sending "
- + r.intent.toString()
- + ", action: " + r.intent.getAction()
- + " from " + r.callerPackage
- + " (uid=" + r.callingUid + ")"
- + " due to receiver " + filter.receiverList.app
- + " (uid " + filter.receiverList.uid + ")"
- + " not specifying RECEIVER_EXPORTED");
- skip = true;
- }
-
- // Filter packages in the intent extras, skipping delivery if none of the packages is
- // visible to the receiver.
- Bundle filteredExtras = null;
- if (!skip && r.filterExtrasForReceiver != null) {
- final Bundle extras = r.intent.getExtras();
- if (extras != null) {
- filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
- if (filteredExtras == null) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Skipping delivery to "
- + filter.receiverList.app
- + " : receiver is filtered by the package visibility");
- }
- skip = true;
- }
- }
- }
-
- if (skip) {
- r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
- return;
- }
-
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
- filter.owningUserId)) {
- r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
- return;
- }
-
- r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
-
- // If this is not being sent as an ordered broadcast, then we
- // don't want to touch the fields that keep track of the current
- // state of ordered broadcasts.
- if (ordered) {
- r.receiver = filter.receiverList.receiver.asBinder();
- r.curFilter = filter;
- filter.receiverList.curBroadcast = r;
- r.state = BroadcastRecord.CALL_IN_RECEIVE;
- if (filter.receiverList.app != null) {
- // Bump hosting application to no longer be in background
- // scheduling class. Note that we can't do that if there
- // isn't an app... but we can only be in that case for
- // things that directly call the IActivityManager API, which
- // are already core system stuff so don't matter for this.
- r.curApp = filter.receiverList.app;
- filter.receiverList.app.mReceivers.addCurReceiver(r);
- mService.enqueueOomAdjTargetLocked(r.curApp);
- mService.updateOomAdjPendingTargetsLocked(
- OOM_ADJ_REASON_START_RECEIVER);
- }
- } else if (filter.receiverList.app != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
- OOM_ADJ_REASON_START_RECEIVER);
- }
-
- try {
- if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
- "Delivering to " + filter + " : " + r);
- if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
- // Skip delivery if full backup in progress
- // If it's an ordered broadcast, we need to continue to the next receiver.
- if (ordered) {
- skipReceiverLocked(r);
- }
- } else {
- r.receiverTime = SystemClock.uptimeMillis();
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
- maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
- maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
- performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId,
- filter.receiverList.uid, r.callingUid,
- r.dispatchTime - r.enqueueTime,
- r.receiverTime - r.dispatchTime);
- // parallel broadcasts are fire-and-forget, not bookended by a call to
- // finishReceiverLocked(), so we manage their activity-start token here
- if (filter.receiverList.app != null
- && r.allowBackgroundActivityStarts && !r.ordered) {
- postActivityStartTokenRemoval(filter.receiverList.app, r);
- }
- }
- if (ordered) {
- r.state = BroadcastRecord.CALL_DONE_RECEIVE;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
- // Clean up ProcessRecord state related to this broadcast attempt
- if (filter.receiverList.app != null) {
- filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
- if (ordered) {
- filter.receiverList.app.mReceivers.removeCurReceiver(r);
- // Something wrong, its oom adj could be downgraded, but not in a hurry.
- mService.enqueueOomAdjTargetLocked(r.curApp);
- }
- }
- // And BroadcastRecord state related to ordered delivery, if appropriate
- if (ordered) {
- r.receiver = null;
- r.curFilter = null;
- filter.receiverList.curBroadcast = null;
- }
- }
- }
-
- private boolean requestStartTargetPermissionsReviewIfNeededLocked(
- BroadcastRecord receiverRecord, String receivingPackageName,
- final int receivingUserId) {
- if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
- receivingPackageName, receivingUserId)) {
- return true;
- }
-
- final boolean callerForeground = receiverRecord.callerApp != null
- ? receiverRecord.callerApp.mState.getSetSchedGroup()
- != ProcessList.SCHED_GROUP_BACKGROUND : true;
-
- // Show a permission review UI only for explicit broadcast from a foreground app
- if (callerForeground && receiverRecord.intent.getComponent() != null) {
- IIntentSender target = mService.mPendingIntentController.getIntentSender(
- ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
- receiverRecord.callerFeatureId, receiverRecord.callingUid,
- receiverRecord.userId, null, null, 0,
- new Intent[]{receiverRecord.intent},
- new String[]{receiverRecord.intent.resolveType(mService.mContext
- .getContentResolver())},
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName);
- intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-
- if (DEBUG_PERMISSIONS_REVIEW) {
- Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package "
- + receivingPackageName);
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId));
- }
- });
- } else {
- Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package"
- + receivingPackageName + " requires a permissions review");
- }
-
- return false;
- }
-
- void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
- @Nullable BroadcastOptions brOptions) {
- if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
- return;
- }
- long duration = brOptions.getTemporaryAppAllowlistDuration();
- final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
- final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
- final String reason = brOptions.getTemporaryAppAllowlistReason();
-
- if (duration > Integer.MAX_VALUE) {
- duration = Integer.MAX_VALUE;
- }
- // XXX ideally we should pause the broadcast until everything behind this is done,
- // or else we will likely start dispatching the broadcast before we have opened
- // access to the app (there is a lot of asynchronicity behind this). It is probably
- // not that big a deal, however, because the main purpose here is to allow apps
- // to hold wake locks, and they will be able to acquire their wake lock immediately
- // it just won't be enabled until we get through this work.
- StringBuilder b = new StringBuilder();
- b.append("broadcast:");
- UserHandle.formatUid(b, r.callingUid);
- b.append(":");
- if (r.intent.getAction() != null) {
- b.append(r.intent.getAction());
- } else if (r.intent.getComponent() != null) {
- r.intent.getComponent().appendShortString(b);
- } else if (r.intent.getData() != null) {
- b.append(r.intent.getData());
- }
- b.append(",reason:");
- b.append(reason);
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
- + " type=" + type + " : " + b.toString());
- }
- mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
- r.callingUid);
- }
+ public abstract void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj);
/**
- * Return true if all given permissions are signature-only perms.
+ * Signal from OS internals that the given package (or some subset of that
+ * package) has been disabled or uninstalled, and that any pending
+ * broadcasts should be cleaned up.
*/
- final boolean isSignaturePerm(String[] perms) {
- if (perms == null) {
- return false;
- }
- IPermissionManager pm = AppGlobals.getPermissionManager();
- for (int i = perms.length-1; i >= 0; i--) {
- try {
- PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
- if (pi == null) {
- // a required permission that no package has actually
- // defined cannot be signature-required.
- return false;
- }
- if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
- | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
- != PermissionInfo.PROTECTION_SIGNATURE) {
- // If this a signature permission and NOT allowed for privileged apps, it
- // is okay... otherwise, nope!
- return false;
- }
- } catch (RemoteException e) {
- return false;
- }
- }
- return true;
- }
-
- private void processNextBroadcast(boolean fromMsg) {
- synchronized (mService) {
- processNextBroadcastLocked(fromMsg, false);
- }
- }
+ public abstract boolean cleanupDisabledPackageReceiversLocked(
+ String packageName, Set<String> filterByClasses, int userId, boolean doit);
- static String broadcastDescription(BroadcastRecord r, ComponentName component) {
- return r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
- }
-
- private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
- @Nullable Bundle filteredExtras) {
- final Intent intent = new Intent(originalIntent);
- if (filteredExtras != null) {
- intent.replaceExtras(filteredExtras);
- }
- return intent;
- }
-
- final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
- BroadcastRecord r;
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
- + mQueueName + "]: "
- + mParallelBroadcasts.size() + " parallel broadcasts; "
- + mDispatcher.describeStateLocked());
-
- mService.updateCpuStats();
-
- if (fromMsg) {
- mBroadcastsScheduled = false;
- }
-
- // First, deliver any non-serialized broadcasts right away.
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchRealTime = SystemClock.elapsedRealtime();
- r.dispatchClockTime = System.currentTimeMillis();
- r.mIsReceiverAppRunning = true;
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
-
- final int N = r.receivers.size();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
- + mQueueName + "] " + r);
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering non-ordered on [" + mQueueName + "] to registered "
- + target + ": " + r);
- deliverToRegisteredReceiverLocked(r,
- (BroadcastFilter) target, false, i);
- }
- addBroadcastToHistoryLocked(r);
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
- + mQueueName + "] " + r);
- }
-
- // Now take care of the next serialized one...
-
- // If we are waiting for a process to come up to handle the next
- // broadcast, then do nothing at this point. Just in case, we
- // check that the process we're waiting for still exists.
- if (mPendingBroadcast != null) {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "processNextBroadcast [" + mQueueName + "]: waiting for "
- + mPendingBroadcast.curApp);
-
- boolean isDead;
- if (mPendingBroadcast.curApp.getPid() > 0) {
- synchronized (mService.mPidsSelfLocked) {
- ProcessRecord proc = mService.mPidsSelfLocked.get(
- mPendingBroadcast.curApp.getPid());
- isDead = proc == null || proc.mErrorState.isCrashing();
- }
- } else {
- final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
- mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
- isDead = proc == null || !proc.isPendingStart();
- }
- if (!isDead) {
- // It's still alive, so keep waiting
- return;
- } else {
- Slog.w(TAG, "pending app ["
- + mQueueName + "]" + mPendingBroadcast.curApp
- + " died before responding to broadcast");
- mPendingBroadcast.state = BroadcastRecord.IDLE;
- mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- }
- }
-
- boolean looped = false;
-
- do {
- final long now = SystemClock.uptimeMillis();
- r = mDispatcher.getNextBroadcastLocked(now);
-
- if (r == null) {
- // No more broadcasts are deliverable right now, so all done!
- mDispatcher.scheduleDeferralCheckLocked(false);
- synchronized (mService.mAppProfiler.mProfilerLock) {
- mService.mAppProfiler.scheduleAppGcsLPf();
- }
- if (looped && !skipOomAdj) {
- // If we had finished the last ordered broadcast, then
- // make sure all processes have correct oom and sched
- // adjustments.
- mService.updateOomAdjPendingTargetsLocked(
- OOM_ADJ_REASON_START_RECEIVER);
- }
-
- // when we have no more ordered broadcast on this queue, stop logging
- if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
- mLogLatencyMetrics = false;
- }
-
- return;
- }
-
- boolean forceReceive = false;
-
- // Ensure that even if something goes awry with the timeout
- // detection, we catch "hung" broadcasts here, discard them,
- // and continue to make progress.
- //
- // This is only done if the system is ready so that early-stage receivers
- // don't get executed with timeouts; and of course other timeout-
- // exempt broadcasts are ignored.
- int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
- if ((numReceivers > 0) &&
- (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
- Slog.w(TAG, "Hung broadcast ["
- + mQueueName + "] discarded after timeout failure:"
- + " now=" + now
- + " dispatchTime=" + r.dispatchTime
- + " startTime=" + r.receiverTime
- + " intent=" + r.intent
- + " numReceivers=" + numReceivers
- + " nextReceiver=" + r.nextReceiver
- + " state=" + r.state);
- broadcastTimeoutLocked(false); // forcibly finish this broadcast
- forceReceive = true;
- r.state = BroadcastRecord.IDLE;
- }
- }
-
- if (r.state != BroadcastRecord.IDLE) {
- if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
- "processNextBroadcast("
- + mQueueName + ") called when not idle (state="
- + r.state + ")");
- return;
- }
-
- // Is the current broadcast is done for any reason?
- if (r.receivers == null || r.nextReceiver >= numReceivers
- || r.resultAbort || forceReceive) {
- // Send the final result if requested
- if (r.resultTo != null) {
- boolean sendResult = true;
-
- // if this was part of a split/deferral complex, update the refcount and only
- // send the completion when we clear all of them
- if (r.splitToken != 0) {
- int newCount = mSplitRefcounts.get(r.splitToken) - 1;
- if (newCount == 0) {
- // done! clear out this record's bookkeeping and deliver
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Sending broadcast completion for split token "
- + r.splitToken + " : " + r.intent.getAction());
- }
- mSplitRefcounts.delete(r.splitToken);
- } else {
- // still have some split broadcast records in flight; update refcount
- // and hold off on the callback
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Result refcount now " + newCount + " for split token "
- + r.splitToken + " : " + r.intent.getAction()
- + " - not sending completion yet");
- }
- sendResult = false;
- mSplitRefcounts.put(r.splitToken, newCount);
- }
- }
- if (sendResult) {
- if (r.callerApp != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
- r.callerApp, OOM_ADJ_REASON_FINISH_RECEIVER);
- }
- try {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
- + r.intent.getAction() + " app=" + r.callerApp);
- }
- if (r.dispatchTime == 0) {
- // The dispatch time here could be 0, in case it's a parallel
- // broadcast but it has a result receiver. Set it to now.
- r.dispatchTime = now;
- }
- r.mIsReceiverAppRunning = true;
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId,
- r.callingUid, r.callingUid,
- r.dispatchTime - r.enqueueTime,
- now - r.dispatchTime);
- logBootCompletedBroadcastCompletionLatencyIfPossible(r);
- // Set this to null so that the reference
- // (local and remote) isn't kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- r.resultTo = null;
- Slog.w(TAG, "Failure ["
- + mQueueName + "] sending broadcast result of "
- + r.intent, e);
- }
- }
- }
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
- cancelBroadcastTimeoutLocked();
-
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Finished with ordered broadcast " + r);
-
- // ... and on to the next...
- addBroadcastToHistoryLocked(r);
- if (r.intent.getComponent() == null && r.intent.getPackage() == null
- && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
- // This was an implicit broadcast... let's record it for posterity.
- mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
- r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
- }
- mDispatcher.retireBroadcastLocked(r);
- r = null;
- looped = true;
- continue;
- }
-
- // Check whether the next receiver is under deferral policy, and handle that
- // accordingly. If the current broadcast was already part of deferred-delivery
- // tracking, we know that it must now be deliverable as-is without re-deferral.
- if (!r.deferred) {
- final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
- if (mDispatcher.isDeferringLocked(receiverUid)) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
- + " at " + r.nextReceiver + " is under deferral");
- }
- // If this is the only (remaining) receiver in the broadcast, "splitting"
- // doesn't make sense -- just defer it as-is and retire it as the
- // currently active outgoing broadcast.
- BroadcastRecord defer;
- if (r.nextReceiver + 1 == numReceivers) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Sole receiver of " + r
- + " is under deferral; setting aside and proceeding");
- }
- defer = r;
- mDispatcher.retireBroadcastLocked(r);
- } else {
- // Nontrivial case; split out 'uid's receivers to a new broadcast record
- // and defer that, then loop and pick up continuing delivery of the current
- // record (now absent those receivers).
-
- // The split operation is guaranteed to match at least at 'nextReceiver'
- defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Post split:");
- Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
- for (int i = 0; i < r.receivers.size(); i++) {
- Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
- }
- Slog.i(TAG_BROADCAST, "Split receivers:");
- for (int i = 0; i < defer.receivers.size(); i++) {
- Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
- }
- }
- // Track completion refcount as well if relevant
- if (r.resultTo != null) {
- int token = r.splitToken;
- if (token == 0) {
- // first split of this record; refcount for 'r' and 'deferred'
- r.splitToken = defer.splitToken = nextSplitTokenLocked();
- mSplitRefcounts.put(r.splitToken, 2);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Broadcast needs split refcount; using new token "
- + r.splitToken);
- }
- } else {
- // new split from an already-refcounted situation; increment count
- final int curCount = mSplitRefcounts.get(token);
- if (DEBUG_BROADCAST_DEFERRAL) {
- if (curCount == 0) {
- Slog.wtf(TAG_BROADCAST,
- "Split refcount is zero with token for " + r);
- }
- }
- mSplitRefcounts.put(token, curCount + 1);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "New split count for token " + token
- + " is " + (curCount + 1));
- }
- }
- }
- }
- mDispatcher.addDeferredBroadcast(receiverUid, defer);
- r = null;
- looped = true;
- continue;
- }
- }
- } while (r == null);
-
- // Get the next receiver...
- int recIdx = r.nextReceiver++;
-
- // Keep track of when this receiver started, and make sure there
- // is a timeout message pending to kill it if need be.
- r.receiverTime = SystemClock.uptimeMillis();
- if (recIdx == 0) {
- r.dispatchTime = r.receiverTime;
- r.dispatchRealTime = SystemClock.elapsedRealtime();
- r.dispatchClockTime = System.currentTimeMillis();
-
- if (mLogLatencyMetrics) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
- r.dispatchClockTime - r.enqueueClockTime);
- }
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
- + mQueueName + "] " + r);
- }
- if (! mPendingBroadcastTimeoutMessage) {
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Submitting BROADCAST_TIMEOUT_MSG ["
- + mQueueName + "] for " + r + " at " + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- }
-
- final BroadcastOptions brOptions = r.options;
- final Object nextReceiver = r.receivers.get(recIdx);
-
- if (nextReceiver instanceof BroadcastFilter) {
- // Simple case: this is a registered receiver who gets
- // a direct call.
- BroadcastFilter filter = (BroadcastFilter)nextReceiver;
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering ordered ["
- + mQueueName + "] to registered "
- + filter + ": " + r);
- r.mIsReceiverAppRunning = true;
- deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
- if (r.receiver == null || !r.ordered) {
- // The receiver has already finished, so schedule to
- // process the next one.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
- + mQueueName + "]: ordered="
- + r.ordered + " receiver=" + r.receiver);
- r.state = BroadcastRecord.IDLE;
- scheduleBroadcastsLocked();
- } else {
- if (filter.receiverList != null) {
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
- // r is guaranteed ordered at this point, so we know finishReceiverLocked()
- // will get a callback and handle the activity start token lifecycle.
- }
- }
- return;
- }
-
- // Hard case: need to instantiate the receiver, possibly
- // starting its application process to host it.
-
- ResolveInfo info =
- (ResolveInfo)nextReceiver;
- ComponentName component = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
-
- boolean skip = false;
- if (brOptions != null &&
- (info.activityInfo.applicationInfo.targetSdkVersion
- < brOptions.getMinManifestReceiverApiLevel() ||
- info.activityInfo.applicationInfo.targetSdkVersion
- > brOptions.getMaxManifestReceiverApiLevel())) {
- Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo
- + " targets " + info.activityInfo.applicationInfo.targetSdkVersion
- + " but delivery restricted to ["
- + brOptions.getMinManifestReceiverApiLevel() + ", "
- + brOptions.getMaxManifestReceiverApiLevel()
- + "] broadcasting " + broadcastDescription(r, component));
- skip = true;
- }
- if (brOptions != null &&
- !brOptions.testRequireCompatChange(info.activityInfo.applicationInfo.uid)) {
- Slog.w(TAG, "Compat change filtered: broadcasting " + broadcastDescription(r, component)
- + " to uid " + info.activityInfo.applicationInfo.uid + " due to compat change "
- + r.options.getRequireCompatChangeId());
- skip = true;
- }
- if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
- component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
- Slog.w(TAG, "Association not allowed: broadcasting "
- + broadcastDescription(r, component));
- skip = true;
- }
- if (!skip) {
- skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
- if (skip) {
- Slog.w(TAG, "Firewall blocked: broadcasting "
- + broadcastDescription(r, component));
- }
- }
- int perm = mService.checkComponentPermission(info.activityInfo.permission,
- r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
- info.activityInfo.exported);
- if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
- if (!info.activityInfo.exported) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + broadcastDescription(r, component)
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid);
- } else {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + broadcastDescription(r, component)
- + " requires " + info.activityInfo.permission);
- }
- skip = true;
- } else if (!skip && info.activityInfo.permission != null) {
- final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
- if (opCode != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(opCode,
- r.callingUid, r.callerPackage, r.callerFeatureId,
- "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + broadcastDescription(r, component)
- + " requires appop " + AppOpsManager.permissionToOp(
- info.activityInfo.permission));
- skip = true;
- }
- }
-
- boolean isSingleton = false;
- try {
- isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo,
- info.activityInfo.name, info.activityInfo.flags);
- } catch (SecurityException e) {
- Slog.w(TAG, e.getMessage());
- skip = true;
- }
- if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
- if (ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- info.activityInfo.applicationInfo.uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
- + " requests FLAG_SINGLE_USER, but app does not hold "
- + android.Manifest.permission.INTERACT_ACROSS_USERS);
- skip = true;
- }
- }
- if (!skip && info.activityInfo.applicationInfo.isInstantApp()
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")"
- + " Instant Apps do not support manifest receivers");
- skip = true;
- }
- if (!skip && r.callerInstantApp
- && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " requires receiver have visibleToInstantApps set"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
- // If the target process is crashing, just skip it.
- Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
- + " to " + r.curApp + ": process crashing");
- skip = true;
- }
- if (!skip) {
- boolean isAvailable = false;
- try {
- isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
- info.activityInfo.packageName,
- UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
- } catch (Exception e) {
- // all such failures mean we skip this receiver
- Slog.w(TAG, "Exception getting recipient info for "
- + info.activityInfo.packageName, e);
- }
- if (!isAvailable) {
- Slog.w(TAG_BROADCAST,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid
- + " : package no longer available");
- skip = true;
- }
- }
-
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (!skip) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
- info.activityInfo.packageName, UserHandle.getUserId(
- info.activityInfo.applicationInfo.uid))) {
- Slog.w(TAG_BROADCAST,
- "Skipping delivery: permission review required for "
- + broadcastDescription(r, component));
- skip = true;
- }
- }
-
- // This is safe to do even if we are skipping the broadcast, and we need
- // this information now to evaluate whether it is going to be allowed to run.
- final int receiverUid = info.activityInfo.applicationInfo.uid;
- // If it's a singleton, it needs to be the same app or a special app
- if (r.callingUid != Process.SYSTEM_UID && isSingleton
- && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
- }
- String targetProcess = info.activityInfo.processName;
- ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid);
-
- if (!skip) {
- final int allowed = mService.getAppStartModeLOSP(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
- if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
- // We won't allow this receiver to be launched if the app has been
- // completely disabled from launches, or it was not explicitly sent
- // to it and the app is in a state that should not receive it
- // (depending on how getAppStartModeLOSP has determined that).
- if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Background execution disabled: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
- || (r.intent.getComponent() == null
- && r.intent.getPackage() == null
- && ((r.intent.getFlags()
- & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
- && !isSignaturePerm(r.requiredPermissions))) {
- mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
- component.getPackageName());
- Slog.w(TAG, "Background execution not allowed: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- }
- }
- }
-
- if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
- && !mService.mUserController
- .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
- 0 /* flags */)) {
- skip = true;
- Slog.w(TAG,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid + " : user is not running");
- }
-
- if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
- for (int i = 0; i < r.excludedPermissions.length; i++) {
- String excludedPermission = r.excludedPermissions[i];
- try {
- perm = AppGlobals.getPackageManager()
- .checkPermission(excludedPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
-
- int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
- if (appOp != AppOpsManager.OP_NONE) {
- // When there is an app op associated with the permission,
- // skip when both the permission and the app op are
- // granted.
- if ((perm == PackageManager.PERMISSION_GRANTED) && (
- mService.getAppOpsManager().checkOpNoThrow(appOp,
- info.activityInfo.applicationInfo.uid,
- info.activityInfo.packageName)
- == AppOpsManager.MODE_ALLOWED)) {
- skip = true;
- break;
- }
- } else {
- // When there is no app op associated with the permission,
- // skip when permission is granted.
- if (perm == PackageManager.PERMISSION_GRANTED) {
- skip = true;
- break;
- }
- }
- }
- }
-
- // Check that the receiver does *not* belong to any of the excluded packages
- if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
- if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) {
- Slog.w(TAG, "Skipping delivery of excluded package "
- + r.intent + " to "
- + component.flattenToShortString()
- + " excludes package " + component.getPackageName()
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
-
- if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- try {
- perm = AppGlobals.getPackageManager().
- checkPermission(requiredPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
- if (!noteOpForManifestReceiver(appOp, r, info, component)) {
- skip = true;
- break;
- }
- }
- }
- }
- if (!skip && r.appOp != AppOpsManager.OP_NONE) {
- if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
- skip = true;
- }
- }
-
- // Filter packages in the intent extras, skipping delivery if none of the packages is
- // visible to the receiver.
- Bundle filteredExtras = null;
- if (!skip && r.filterExtrasForReceiver != null) {
- final Bundle extras = r.intent.getExtras();
- if (extras != null) {
- filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
- if (filteredExtras == null) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Skipping delivery to "
- + info.activityInfo.packageName + " / " + receiverUid
- + " : receiver is filtered by the package visibility");
- }
- skip = true;
- }
- }
- }
-
- if (skip) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery of ordered [" + mQueueName + "] "
- + r + " for reason described above");
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
- r.receiver = null;
- r.curFilter = null;
- r.state = BroadcastRecord.IDLE;
- r.manifestSkipCount++;
- scheduleBroadcastsLocked();
- return;
- }
- r.manifestCount++;
-
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
- r.state = BroadcastRecord.APP_RECEIVE;
- r.curComponent = component;
- r.curReceiver = info.activityInfo;
- r.curFilteredExtras = filteredExtras;
- if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
- Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
- + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
- + receiverUid);
- }
- final boolean isActivityCapable =
- (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
- maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
-
- // Report that a component is used for explicit broadcasts.
- if (r.intent.getComponent() != null && r.curComponent != null
- && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
- mService.mUsageStatsService.reportEvent(
- r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
- }
-
- // Broadcast is being executed, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, r.userId);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.curComponent.getPackageName() + ": " + e);
- }
-
- // Is this receiver's application already running?
- if (app != null && app.getThread() != null && !app.isKilled()) {
- try {
- app.addPackage(info.activityInfo.packageName,
- info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
- maybeAddAllowBackgroundActivityStartsToken(app, r);
- r.mIsReceiverAppRunning = true;
- processCurBroadcastLocked(r, app);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Failed sending broadcast to "
- + r.curComponent + " with " + r.intent, e);
- // If some unexpected exception happened, just skip
- // this broadcast. At this point we are not in the call
- // from a client, so throwing an exception out from here
- // will crash the entire system instead of just whoever
- // sent the broadcast.
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- // Not running -- get it started, to be executed when the app comes up.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Need to start app ["
- + mQueueName + "] " + targetProcess + " for broadcast " + r);
- r.curApp = mService.startProcessLocked(targetProcess,
- info.activityInfo.applicationInfo, true,
- r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction(), getHostingRecordTriggerType(r)),
- isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
- (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
- if (r.curApp == null) {
- // Ah, this recipient is unavailable. Finish it if necessary,
- // and mark the broadcast record as ready for the next.
- Slog.w(TAG, "Unable to launch app "
- + info.activityInfo.applicationInfo.packageName + "/"
- + receiverUid + " for broadcast "
- + r.intent + ": process is bad");
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
- }
-
- private String getHostingRecordTriggerType(BroadcastRecord r) {
- if (r.alarm) {
- return HostingRecord.TRIGGER_TYPE_ALARM;
- } else if (r.pushMessage) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
- } else if (r.pushMessageOverQuota) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
- }
- return HostingRecord.TRIGGER_TYPE_UNKNOWN;
- }
-
- @Nullable
- private String getTargetPackage(BroadcastRecord r) {
- if (r.intent == null) {
- return null;
- }
- if (r.intent.getPackage() != null) {
- return r.intent.getPackage();
- } else if (r.intent.getComponent() != null) {
- return r.intent.getComponent().getPackageName();
- }
- return null;
- }
-
- private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
- // Only log after last receiver.
- // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
- // last BroadcastRecord of the split broadcast which has non-null resultTo.
- final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (r.nextReceiver < numReceivers) {
- return;
- }
- final String action = r.intent.getAction();
- int event = 0;
- if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
- event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
- }
- if (event != 0) {
- final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
- final int completeLatency = (int)
- (SystemClock.uptimeMillis() - r.enqueueTime);
- final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
- final int completeRealLatency = (int)
- (SystemClock.elapsedRealtime() - r.enqueueRealTime);
- int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
- // This method is called very infrequently, no performance issue we call
- // LocalServices.getService() here.
- final UserManagerInternal umInternal = LocalServices.getService(
- UserManagerInternal.class);
- final UserInfo userInfo = umInternal.getUserInfo(r.userId);
- if (userInfo != null) {
- userType = UserManager.getUserTypeForStatsd(userInfo.userType);
- }
- Slog.i(TAG_BROADCAST,
- "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
- + action
- + " dispatchLatency:" + dispatchLatency
- + " completeLatency:" + completeLatency
- + " dispatchRealLatency:" + dispatchRealLatency
- + " completeRealLatency:" + completeRealLatency
- + " receiversSize:" + numReceivers
- + " userId:" + r.userId
- + " userType:" + (userInfo != null? userInfo.userType : null));
- FrameworkStatsLog.write(
- BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
- event,
- dispatchLatency,
- completeLatency,
- dispatchRealLatency,
- completeRealLatency,
- r.userId,
- userType);
- }
- }
-
- private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
- if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
- return;
- }
- final String targetPackage = getTargetPackage(r);
- // Ignore non-explicit broadcasts
- if (targetPackage == null) {
- return;
- }
- getUsageStatsManagerInternal().reportBroadcastDispatched(
- r.callingUid, targetPackage, UserHandle.of(r.userId),
- r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
- mService.getUidStateLocked(targetUid));
- }
-
- @NonNull
- private UsageStatsManagerInternal getUsageStatsManagerInternal() {
- final UsageStatsManagerInternal usageStatsManagerInternal =
- LocalServices.getService(UsageStatsManagerInternal.class);
- return usageStatsManagerInternal;
- }
-
- private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
- ComponentName component) {
- if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {
- return noteOpForManifestReceiverInner(appOp, r, info, component, null);
- } else {
- // Attribution tags provided, noteOp each tag
- for (String tag : info.activityInfo.attributionTags) {
- if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
- return false;
- }
- }
- return true;
- }
- }
-
- private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
- ComponentName component, String tag) {
- if (mService.getAppOpsManager().noteOpNoThrow(appOp,
- info.activityInfo.applicationInfo.uid,
- info.activityInfo.packageName,
- tag,
- "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.opToName(appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- return false;
- }
- return true;
- }
-
- private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
- if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
- return;
- }
- String msgToken = (proc.toShortString() + r.toString()).intern();
- // first, if there exists a past scheduled request to remove this token, drop
- // that request - we don't want the token to be swept from under our feet...
- mHandler.removeCallbacksAndMessages(msgToken);
- // ...then add the token
- proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
- }
-
- final void setBroadcastTimeoutLocked(long timeoutTime) {
- if (! mPendingBroadcastTimeoutMessage) {
- Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
- mHandler.sendMessageAtTime(msg, timeoutTime);
- mPendingBroadcastTimeoutMessage = true;
- }
- }
-
- final void cancelBroadcastTimeoutLocked() {
- if (mPendingBroadcastTimeoutMessage) {
- mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
- mPendingBroadcastTimeoutMessage = false;
- }
- }
-
- final void broadcastTimeoutLocked(boolean fromMsg) {
- if (fromMsg) {
- mPendingBroadcastTimeoutMessage = false;
- }
-
- if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
- if (fromMsg) {
- if (!mService.mProcessesReady) {
- // Only process broadcast timeouts if the system is ready; some early
- // broadcasts do heavy work setting up system facilities
- return;
- }
-
- // If the broadcast is generally exempt from timeout tracking, we're done
- if (r.timeoutExempt) {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
- + r.intent.getAction());
- }
- return;
- }
-
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- if (timeoutTime > now) {
- // We can observe premature timeouts because we do not cancel and reset the
- // broadcast timeout message after each receiver finishes. Instead, we set up
- // an initial timeout then kick it down the road a little further as needed
- // when it expires.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Premature timeout ["
- + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- return;
- }
- }
-
- if (r.state == BroadcastRecord.WAITING_SERVICES) {
- // In this case the broadcast had already finished, but we had decided to wait
- // for started services to finish as well before going on. So if we have actually
- // waited long enough time timeout the broadcast, let's give up on the whole thing
- // and just move on to the next.
- Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
- ? r.curComponent.flattenToShortString() : "(null)"));
- r.curComponent = null;
- r.state = BroadcastRecord.IDLE;
- processNextBroadcastLocked(false, false);
- return;
- }
-
- // If the receiver app is being debugged we quietly ignore unresponsiveness, just
- // tidying up and moving on to the next broadcast without crashing or ANRing this
- // app just because it's stopped at a breakpoint.
- final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
-
- long timeoutDurationMs = now - r.receiverTime;
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
- + ", started " + timeoutDurationMs + "ms ago");
- r.receiverTime = now;
- if (!debugging) {
- r.anrCount++;
- }
-
- ProcessRecord app = null;
- TimeoutRecord timeoutRecord = null;
-
- Object curReceiver;
- if (r.nextReceiver > 0) {
- curReceiver = r.receivers.get(r.nextReceiver-1);
- r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
- } else {
- curReceiver = r.curReceiver;
- }
- Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
- logBroadcastReceiverDiscardLocked(r);
- if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != ActivityManagerService.MY_PID) {
- synchronized (mService.mPidsSelfLocked) {
- app = mService.mPidsSelfLocked.get(
- bf.receiverList.pid);
- }
- }
- } else {
- app = r.curApp;
- }
-
- if (app != null) {
- String anrMessage =
- "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
- + "ms";
- timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
- }
-
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
- }
-
- // Move on to the next receiver.
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
-
- if (!debugging && timeoutRecord != null) {
- mService.mAnrHelper.appNotResponding(app, timeoutRecord);
- }
- }
-
- private final int ringAdvance(int x, final int increment, final int ringSize) {
- x += increment;
- if (x < 0) return (ringSize - 1);
- else if (x >= ringSize) return 0;
- else return x;
- }
-
- private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
- if (original.callingUid < 0) {
- // This was from a registerReceiver() call; ignore it.
- return;
- }
- original.finishTime = SystemClock.uptimeMillis();
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(original));
- }
-
- final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
- final String callerPackage = info != null ? info.packageName : original.callerPackage;
- if (callerPackage != null) {
- mService.mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
- original.callingUid, 0, callerPackage).sendToTarget();
- }
-
- // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
- // So don't change the incoming record directly.
- final BroadcastRecord historyRecord = original.maybeStripForHistory();
-
- mBroadcastHistory[mHistoryNext] = historyRecord;
- mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
-
- mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
- mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
- mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
- mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
- mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
- }
-
- boolean cleanupDisabledPackageReceiversLocked(
- String packageName, Set<String> filterByClasses, int userId, boolean doit) {
- boolean didSomething = false;
- for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
- didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
- packageName, filterByClasses, userId, doit);
- if (!doit && didSomething) {
- return true;
- }
- }
-
- didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
- filterByClasses, userId, doit);
-
- return didSomething;
- }
-
- final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- final int logIndex = r.nextReceiver - 1;
- if (logIndex >= 0 && logIndex < r.receivers.size()) {
- Object curReceiver = r.receivers.get(logIndex);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
- bf.owningUserId, System.identityHashCode(r),
- r.intent.getAction(), logIndex, System.identityHashCode(bf));
- } else {
- ResolveInfo ri = (ResolveInfo) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
- System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
- }
- } else {
- if (logIndex < 0) Slog.w(TAG,
- "Discarding broadcast before first receiver is invoked: " + r);
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- -1, System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver,
- "NONE");
- }
- }
-
- private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
- return formatSimple("Broadcast %s from %s (%s) %s",
- state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
- record.callerPackage == null ? "" : record.callerPackage,
- record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
- record.intent == null ? "" : record.intent.getAction());
- }
-
- boolean isIdle() {
- return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
- && (mPendingBroadcast == null);
- }
-
- // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
- // be immediately deliverable.
- void cancelDeferrals() {
- synchronized (mService) {
- mDispatcher.cancelDeferralsLocked();
- scheduleBroadcastsLocked();
- }
- }
-
- String describeState() {
- synchronized (mService) {
- return mParallelBroadcasts.size() + " parallel; "
- + mDispatcher.describeStateLocked();
- }
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
- int N;
- N = mParallelBroadcasts.size();
- for (int i = N - 1; i >= 0; i--) {
- mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
- }
- mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
- if (mPendingBroadcast != null) {
- mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
- }
-
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r != null) {
- r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
- }
- } while (ringIndex != lastIndex);
-
- lastIndex = ringIndex = mSummaryHistoryNext;
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
- intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
- false, true, true, false);
- proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
- mSummaryHistoryEnqueueTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
- mSummaryHistoryDispatchTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
- mSummaryHistoryFinishTime[ringIndex]);
- proto.end(summaryToken);
- } while (ringIndex != lastIndex);
- proto.end(token);
- }
-
- final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
- || mPendingBroadcast != null) {
- boolean printed = false;
- for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
- BroadcastRecord br = mParallelBroadcasts.get(i);
- if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- printed = true;
- pw.println(" Active broadcasts [" + mQueueName + "]:");
- }
- pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
- br.dump(pw, " ", sdf);
- }
-
- mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
-
- if (dumpPackage == null || (mPendingBroadcast != null
- && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
- pw.println();
- pw.println(" Pending broadcast [" + mQueueName + "]:");
- if (mPendingBroadcast != null) {
- mPendingBroadcast.dump(pw, " ", sdf);
- } else {
- pw.println(" (null)");
- }
- needSep = true;
- }
- }
-
- mConstants.dump(pw);
-
- int i;
- boolean printed = false;
+ /**
+ * Quickly determine if this queue has broadcasts that are still waiting to
+ * be delivered at some point in the future.
+ *
+ * @see #flush()
+ */
+ public abstract boolean isIdle();
- i = -1;
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
+ /**
+ * Brief summary of internal state, useful for debugging purposes.
+ */
+ public abstract String describeState();
- i++; // genuine record of some sort even if we're filtering it out
- if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts [" + mQueueName + "]:");
- printed = true;
- }
- if (dumpAll) {
- pw.print(" Historical Broadcast " + mQueueName + " #");
- pw.print(i); pw.println(":");
- r.dump(pw, " ", sdf);
- } else {
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
- pw.print(" ");
- pw.println(r.intent.toShortString(false, true, true, false));
- if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
- pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
- }
- Bundle bundle = r.intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- }
- } while (ringIndex != lastIndex);
+ /**
+ * Flush any broadcasts still waiting to be delivered, causing them to be
+ * delivered as soon as possible.
+ *
+ * @see #isIdle()
+ */
+ public abstract void flush();
- if (dumpPackage == null) {
- lastIndex = ringIndex = mSummaryHistoryNext;
- if (dumpAll) {
- printed = false;
- i = -1;
- } else {
- // roll over the 'i' full dumps that have already been issued
- for (int j = i;
- j > 0 && ringIndex != lastIndex;) {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
- j--;
- }
- }
- // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
- // the overall broadcast history.
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
- printed = true;
- }
- if (!dumpAll && i >= 50) {
- pw.println(" ...");
- break;
- }
- i++;
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(intent.toShortString(false, true, true, false));
- pw.print(" ");
- TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
- - mSummaryHistoryEnqueueTime[ringIndex], pw);
- pw.print(" dispatch ");
- TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
- - mSummaryHistoryDispatchTime[ringIndex], pw);
- pw.println(" finish");
- pw.print(" enq=");
- pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
- pw.print(" disp=");
- pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
- pw.print(" fin=");
- pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- } while (ringIndex != lastIndex);
- }
+ public abstract void dumpDebug(ProtoOutputStream proto, long fieldId);
- return needSep;
- }
+ public abstract boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean needSep);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
new file mode 100644
index 000000000000..4bffe351a379
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -0,0 +1,1960 @@
+/*
+ * Copyright (C) 2012 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.am;
+
+import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.text.TextUtils.formatSimple;
+
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
+import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.os.TimeoutRecord;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * BROADCASTS
+ *
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
+ */
+public class BroadcastQueueImpl extends BroadcastQueue {
+ private static final String TAG_MU = TAG + POSTFIX_MU;
+ private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
+
+ static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
+ static final int MAX_BROADCAST_SUMMARY_HISTORY
+ = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
+
+ /**
+ * If true, we can delay broadcasts while waiting services to finish in the previous
+ * receiver's process.
+ */
+ final boolean mDelayBehindServices;
+
+ /**
+ * Lists of all active broadcasts that are to be executed immediately
+ * (without waiting for another broadcast to finish). Currently this only
+ * contains broadcasts to registered receivers, to avoid spinning up
+ * a bunch of processes to execute IntentReceiver components. Background-
+ * and foreground-priority broadcasts are queued separately.
+ */
+ final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
+
+ /**
+ * Tracking of the ordered broadcast queue, including deferral policy and alarm
+ * prioritization.
+ */
+ final BroadcastDispatcher mDispatcher;
+
+ /**
+ * Refcounting for completion callbacks of split/deferred broadcasts. The key
+ * is an opaque integer token assigned lazily when a broadcast is first split
+ * into multiple BroadcastRecord objects.
+ */
+ final SparseIntArray mSplitRefcounts = new SparseIntArray();
+ private int mNextToken = 0;
+
+ /**
+ * Historical data of past broadcasts, for debugging. This is a ring buffer
+ * whose last element is at mHistoryNext.
+ */
+ final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+ int mHistoryNext = 0;
+
+ /**
+ * Summary of historical data of past broadcasts, for debugging. This is a
+ * ring buffer whose last element is at mSummaryHistoryNext.
+ */
+ final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+ int mSummaryHistoryNext = 0;
+
+ /**
+ * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
+ * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
+ * clock time, not elapsed.
+ */
+ final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+
+ /**
+ * Set when we current have a BROADCAST_INTENT_MSG in flight.
+ */
+ boolean mBroadcastsScheduled = false;
+
+ /**
+ * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+ */
+ boolean mPendingBroadcastTimeoutMessage;
+
+ /**
+ * Intent broadcasts that we have tried to start, but are
+ * waiting for the application's process to be created. We only
+ * need one per scheduling class (instead of a list) because we always
+ * process broadcasts one at a time, so no others can be started while
+ * waiting for this one.
+ */
+ BroadcastRecord mPendingBroadcast = null;
+
+ /**
+ * The receiver index that is pending, to restart the broadcast if needed.
+ */
+ int mPendingBroadcastRecvIndex;
+
+ static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
+ static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+
+ // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
+ boolean mLogLatencyMetrics = true;
+
+ final BroadcastHandler mHandler;
+
+ private final class BroadcastHandler extends Handler {
+ public BroadcastHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BROADCAST_INTENT_MSG: {
+ if (DEBUG_BROADCAST) Slog.v(
+ TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ + mQueueName + "]");
+ processNextBroadcast(true);
+ } break;
+ case BROADCAST_TIMEOUT_MSG: {
+ synchronized (mService) {
+ broadcastTimeoutLocked(true);
+ }
+ } break;
+ }
+ }
+ }
+
+ BroadcastQueueImpl(ActivityManagerService service, Handler handler,
+ String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
+ super(service, handler, name, constants);
+ mHandler = new BroadcastHandler(handler.getLooper());
+ mDelayBehindServices = allowDelayBehindServices;
+ mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
+ }
+
+ void start(ContentResolver resolver) {
+ mDispatcher.start();
+ mConstants.startObserving(mHandler, resolver);
+ }
+
+ public boolean isDelayBehindServices() {
+ return mDelayBehindServices;
+ }
+
+ public BroadcastRecord getPendingBroadcastLocked() {
+ return mPendingBroadcast;
+ }
+
+ public BroadcastRecord getActiveBroadcastLocked() {
+ return mDispatcher.getActiveBroadcastLocked();
+ }
+
+ public void enqueueBroadcastLocked(BroadcastRecord r) {
+ final boolean replacePending = (r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
+
+ // Ordered broadcasts obviously need to be dispatched in serial order,
+ // but this implementation expects all manifest receivers to also be
+ // dispatched in a serial fashion
+ boolean serialDispatch = r.ordered;
+ if (!serialDispatch) {
+ final int N = (r.receivers != null) ? r.receivers.size() : 0;
+ for (int i = 0; i < N; i++) {
+ if (r.receivers.get(i) instanceof ResolveInfo) {
+ serialDispatch = true;
+ break;
+ }
+ }
+ }
+
+ if (serialDispatch) {
+ final BroadcastRecord oldRecord =
+ replacePending ? replaceOrderedBroadcastLocked(r) : null;
+ if (oldRecord != null) {
+ // Replaced, fire the result-to receiver.
+ if (oldRecord.resultTo != null) {
+ try {
+ oldRecord.mIsReceiverAppRunning = true;
+ performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
+ oldRecord.intent,
+ Activity.RESULT_CANCELED, null, null,
+ false, false, oldRecord.userId, oldRecord.callingUid, r.callingUid,
+ SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + oldRecord.intent, e);
+
+ }
+ }
+ } else {
+ enqueueOrderedBroadcastLocked(r);
+ scheduleBroadcastsLocked();
+ }
+ } else {
+ final boolean replaced = replacePending
+ && (replaceParallelBroadcastLocked(r) != null);
+ // Note: We assume resultTo is null for non-ordered broadcasts.
+ if (!replaced) {
+ enqueueParallelBroadcastLocked(r);
+ scheduleBroadcastsLocked();
+ }
+ }
+ }
+
+ public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
+ r.enqueueTime = SystemClock.uptimeMillis();
+ r.enqueueRealTime = SystemClock.elapsedRealtime();
+ mParallelBroadcasts.add(r);
+ enqueueBroadcastHelper(r);
+ }
+
+ public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
+ r.enqueueTime = SystemClock.uptimeMillis();
+ r.enqueueRealTime = SystemClock.elapsedRealtime();
+ mDispatcher.enqueueOrderedBroadcastLocked(r);
+ enqueueBroadcastHelper(r);
+ }
+
+ /**
+ * Don't call this method directly; call enqueueParallelBroadcastLocked or
+ * enqueueOrderedBroadcastLocked.
+ */
+ private void enqueueBroadcastHelper(BroadcastRecord r) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ }
+ }
+
+ /**
+ * Find the same intent from queued parallel broadcast, replace with a new one and return
+ * the old one.
+ */
+ public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
+ return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
+ }
+
+ /**
+ * Find the same intent from queued ordered broadcast, replace with a new one and return
+ * the old one.
+ */
+ public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
+ return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
+ }
+
+ private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
+ BroadcastRecord r, String typeForLogging) {
+ final Intent intent = r.intent;
+ for (int i = queue.size() - 1; i > 0; i--) {
+ final BroadcastRecord old = queue.get(i);
+ if (old.userId == r.userId && intent.filterEquals(old.intent)) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG_BROADCAST, "***** DROPPING "
+ + typeForLogging + " [" + mQueueName + "]: " + intent);
+ }
+ queue.set(i, r);
+ return old;
+ }
+ }
+ return null;
+ }
+
+ private final void processCurBroadcastLocked(BroadcastRecord r,
+ ProcessRecord app) throws RemoteException {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + " for app " + app);
+ final IApplicationThread thread = app.getThread();
+ if (thread == null) {
+ throw new RemoteException();
+ }
+ if (app.isInFullBackup()) {
+ skipReceiverLocked(r);
+ return;
+ }
+
+ r.receiver = thread.asBinder();
+ r.curApp = app;
+ final ProcessReceiverRecord prr = app.mReceivers;
+ prr.addCurReceiver(r);
+ app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+ // Don't bump its LRU position if it's in the background restricted.
+ if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
+ < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
+ mService.updateLruProcessLocked(app, false, null);
+ }
+ // Make sure the oom adj score is updated before delivering the broadcast.
+ // Force an update, even if there are other pending requests, overall it still saves time,
+ // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
+ mService.enqueueOomAdjTargetLocked(app);
+ mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+
+ // Tell the application to launch this receiver.
+ maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
+ r.intent.setComponent(r.curComponent);
+
+ boolean started = false;
+ try {
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "Delivering to component " + r.curComponent
+ + ": " + r);
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+ PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
+ thread.scheduleReceiver(prepareReceiverIntent(r.intent, r.curFilteredExtras),
+ r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+ app.mState.getReportedProcState());
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + " DELIVERED for app " + app);
+ started = true;
+ } finally {
+ if (!started) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + ": NOT STARTED!");
+ r.receiver = null;
+ r.curApp = null;
+ prr.removeCurReceiver(r);
+ }
+ }
+
+ // if something bad happens here, launch the app and try again
+ if (app.isKilled()) {
+ throw new RemoteException("app gets killed during broadcasting");
+ }
+ }
+
+ /**
+ * Called by ActivityManagerService to notify that the uid has process started, if there is any
+ * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
+ * @param uid
+ */
+ public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+ mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
+ scheduleBroadcastsLocked();
+ }
+
+ public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ boolean didSomething = false;
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
+ if (br.curApp != app) {
+ Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ + app.processName + ", intended target is " + br.curApp.processName);
+ return false;
+ }
+ try {
+ mPendingBroadcast = null;
+ br.mIsReceiverAppRunning = false;
+ processCurBroadcastLocked(br, app);
+ didSomething = true;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting receiver "
+ + br.curComponent.flattenToShortString(), e);
+ logBroadcastReceiverDiscardLocked(br);
+ finishReceiverLocked(br, br.resultCode, br.resultData,
+ br.resultExtras, br.resultAbort, false);
+ scheduleBroadcastsLocked();
+ // We need to reset the state if we failed to start the receiver.
+ br.state = BroadcastRecord.IDLE;
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ return didSomething;
+ }
+
+ public void skipPendingBroadcastLocked(int pid) {
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.getPid() == pid) {
+ br.state = BroadcastRecord.IDLE;
+ br.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ // Skip the current receiver, if any, that is in flight to the given process
+ public void skipCurrentReceiverLocked(ProcessRecord app) {
+ BroadcastRecord r = null;
+ final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
+ if (curActive != null && curActive.curApp == app) {
+ // confirmed: the current active broadcast is to the given app
+ r = curActive;
+ }
+
+ // If the current active broadcast isn't this BUT we're waiting for
+ // mPendingBroadcast to spin up the target app, that's what we use.
+ if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "[" + mQueueName + "] skip & discard pending app " + r);
+ r = mPendingBroadcast;
+ }
+
+ if (r != null) {
+ skipReceiverLocked(r);
+ }
+ }
+
+ private void skipReceiverLocked(BroadcastRecord r) {
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ }
+
+ public void scheduleBroadcastsLocked() {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ + mQueueName + "]: current="
+ + mBroadcastsScheduled);
+
+ if (mBroadcastsScheduled) {
+ return;
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
+ mBroadcastsScheduled = true;
+ }
+
+ public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null && br.receiver == receiver) {
+ return br;
+ }
+ return null;
+ }
+
+ // > 0 only, no worry about "eventual" recycling
+ private int nextSplitTokenLocked() {
+ int next = mNextToken + 1;
+ if (next <= 0) {
+ next = 1;
+ }
+ mNextToken = next;
+ return next;
+ }
+
+ private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
+ // the receiver had run for less than allowed bg activity start timeout,
+ // so allow the process to still start activities from bg for some more time
+ String msgToken = (app.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then schedule the removal of the token after the extended timeout
+ mHandler.postAtTime(() -> {
+ synchronized (mService) {
+ app.removeAllowBackgroundActivityStartsToken(r);
+ }
+ }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+ }
+
+ public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
+ final int state = r.state;
+ final ActivityInfo receiver = r.curReceiver;
+ final long finishTime = SystemClock.uptimeMillis();
+ final long elapsed = finishTime - r.receiverTime;
+ r.state = BroadcastRecord.IDLE;
+ final int curIndex = r.nextReceiver - 1;
+ if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
+ final Object curReceiver = r.receivers.get(curIndex);
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
+ r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
+ ActivityManagerService.getShortAction(r.intent.getAction()),
+ curReceiver instanceof BroadcastFilter
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
+ : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
+ r.mIsReceiverAppRunning
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
+ : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime,
+ finishTime - r.receiverTime);
+ }
+ if (state == BroadcastRecord.IDLE) {
+ Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+ }
+ if (r.allowBackgroundActivityStarts && r.curApp != null) {
+ if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
+ // if the receiver has run for more than allowed bg activity start timeout,
+ // just remove the token for this process now and we're done
+ r.curApp.removeAllowBackgroundActivityStartsToken(r);
+ } else {
+ // It gets more time; post the removal to happen at the appropriate moment
+ postActivityStartTokenRemoval(r.curApp, r);
+ }
+ }
+ // If we're abandoning this broadcast before any receivers were actually spun up,
+ // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
+ if (r.nextReceiver > 0) {
+ r.duration[r.nextReceiver - 1] = elapsed;
+ }
+
+ // if this receiver was slow, impose deferral policy on the app. This will kick in
+ // when processNextBroadcastLocked() next finds this uid as a receiver identity.
+ if (!r.timeoutExempt) {
+ // r.curApp can be null if finish has raced with process death - benign
+ // edge case, and we just ignore it because we're already cleaning up
+ // as expected.
+ if (r.curApp != null
+ && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+ // Core system packages are exempt from deferral policy
+ if (!UserHandle.isCore(r.curApp.uid)) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
+ + " was slow: " + receiver + " br=" + r);
+ }
+ mDispatcher.startDeferring(r.curApp.uid);
+ } else {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
+ + " receiver was slow but not deferring: "
+ + receiver + " br=" + r);
+ }
+ }
+ }
+ } else {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
+ + " is exempt from deferral policy");
+ }
+ }
+
+ r.receiver = null;
+ r.intent.setComponent(null);
+ if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
+ r.curApp.mReceivers.removeCurReceiver(r);
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ }
+ if (r.curFilter != null) {
+ r.curFilter.receiverList.curBroadcast = null;
+ }
+ r.curFilter = null;
+ r.curReceiver = null;
+ r.curApp = null;
+ r.curFilteredExtras = null;
+ mPendingBroadcast = null;
+
+ r.resultCode = resultCode;
+ r.resultData = resultData;
+ r.resultExtras = resultExtras;
+ if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
+ r.resultAbort = resultAbort;
+ } else {
+ r.resultAbort = false;
+ }
+
+ // If we want to wait behind services *AND* we're finishing the head/
+ // active broadcast on its queue
+ if (waitForServices && r.curComponent != null && r.queue.isDelayBehindServices()
+ && r.queue.getActiveBroadcastLocked() == r) {
+ ActivityInfo nextReceiver;
+ if (r.nextReceiver < r.receivers.size()) {
+ Object obj = r.receivers.get(r.nextReceiver);
+ nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
+ } else {
+ nextReceiver = null;
+ }
+ // Don't do this if the next receive is in the same process as the current one.
+ if (receiver == null || nextReceiver == null
+ || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
+ || !receiver.processName.equals(nextReceiver.processName)) {
+ // In this case, we are ready to process the next receiver for the current broadcast,
+ // but are on a queue that would like to wait for services to finish before moving
+ // on. If there are background services currently starting, then we will go into a
+ // special state where we hold off on continuing this broadcast until they are done.
+ if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
+ Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
+ r.state = BroadcastRecord.WAITING_SERVICES;
+ return false;
+ }
+ }
+ }
+
+ r.curComponent = null;
+
+ // We will process the next receiver right now if this is finishing
+ // an app receiver (which is always asynchronous) or after we have
+ // come back from calling a receiver.
+ return state == BroadcastRecord.APP_RECEIVE
+ || state == BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+
+ public void backgroundServicesFinishedLocked(int userId) {
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null) {
+ if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
+ Slog.i(TAG, "Resuming delayed broadcast");
+ br.curComponent = null;
+ br.state = BroadcastRecord.IDLE;
+ processNextBroadcastLocked(false, false);
+ }
+ }
+ }
+
+ public void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
+ Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser,
+ int receiverUid, int callingUid, long dispatchDelay,
+ long receiveDelay) throws RemoteException {
+ // Send the intent to the receiver asynchronously using one-way binder calls.
+ if (app != null) {
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ // If we have an app thread, do the call through that so it is
+ // correctly ordered with other one-way calls.
+ try {
+ thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
+ data, extras, ordered, sticky, sendingUser,
+ app.mState.getReportedProcState());
+ // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
+ // DeadObjectException when the process isn't actually dead.
+ //} catch (DeadObjectException ex) {
+ // Failed to call into the process. It's dying so just let it die and move on.
+ // throw ex;
+ } catch (RemoteException ex) {
+ // Failed to call into the process. It's either dying or wedged. Kill it gently.
+ synchronized (mService) {
+ Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ + " (pid " + app.getPid() + "). Crashing it.");
+ app.scheduleCrashLocked("can't deliver broadcast",
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
+ }
+ throw ex;
+ }
+ } else {
+ // Application has died. Receiver doesn't exist.
+ throw new RemoteException("app.thread must not be null");
+ }
+ } else {
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
+ }
+ if (!ordered) {
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
+ receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
+ callingUid == -1 ? Process.SYSTEM_UID : callingUid,
+ ActivityManagerService.getShortAction(intent.getAction()),
+ BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
+ BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
+ dispatchDelay, receiveDelay, 0 /* finish_delay */);
+ }
+ }
+
+ private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
+ BroadcastFilter filter, boolean ordered, int index) {
+ boolean skip = mSkipPolicy.shouldSkip(r, filter);
+
+ // Filter packages in the intent extras, skipping delivery if none of the packages is
+ // visible to the receiver.
+ Bundle filteredExtras = null;
+ if (!skip && r.filterExtrasForReceiver != null) {
+ final Bundle extras = r.intent.getExtras();
+ if (extras != null) {
+ filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
+ if (filteredExtras == null) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Skipping delivery to "
+ + filter.receiverList.app
+ + " : receiver is filtered by the package visibility");
+ }
+ skip = true;
+ }
+ }
+ }
+
+ if (skip) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
+ }
+
+ r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
+
+ // If this is not being sent as an ordered broadcast, then we
+ // don't want to touch the fields that keep track of the current
+ // state of ordered broadcasts.
+ if (ordered) {
+ r.receiver = filter.receiverList.receiver.asBinder();
+ r.curFilter = filter;
+ filter.receiverList.curBroadcast = r;
+ r.state = BroadcastRecord.CALL_IN_RECEIVE;
+ if (filter.receiverList.app != null) {
+ // Bump hosting application to no longer be in background
+ // scheduling class. Note that we can't do that if there
+ // isn't an app... but we can only be in that case for
+ // things that directly call the IActivityManager API, which
+ // are already core system stuff so don't matter for this.
+ r.curApp = filter.receiverList.app;
+ filter.receiverList.app.mReceivers.addCurReceiver(r);
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ mService.updateOomAdjPendingTargetsLocked(
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+ } else if (filter.receiverList.app != null) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+
+ try {
+ if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
+ "Delivering to " + filter + " : " + r);
+ if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
+ // Skip delivery if full backup in progress
+ // If it's an ordered broadcast, we need to continue to the next receiver.
+ if (ordered) {
+ skipReceiverLocked(r);
+ }
+ } else {
+ r.receiverTime = SystemClock.uptimeMillis();
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
+ maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+ prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId,
+ filter.receiverList.uid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime);
+ // parallel broadcasts are fire-and-forget, not bookended by a call to
+ // finishReceiverLocked(), so we manage their activity-start token here
+ if (filter.receiverList.app != null
+ && r.allowBackgroundActivityStarts && !r.ordered) {
+ postActivityStartTokenRemoval(filter.receiverList.app, r);
+ }
+ }
+ if (ordered) {
+ r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+ // Clean up ProcessRecord state related to this broadcast attempt
+ if (filter.receiverList.app != null) {
+ filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
+ if (ordered) {
+ filter.receiverList.app.mReceivers.removeCurReceiver(r);
+ // Something wrong, its oom adj could be downgraded, but not in a hurry.
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ }
+ }
+ // And BroadcastRecord state related to ordered delivery, if appropriate
+ if (ordered) {
+ r.receiver = null;
+ r.curFilter = null;
+ filter.receiverList.curBroadcast = null;
+ }
+ }
+ }
+
+ void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
+ @Nullable BroadcastOptions brOptions) {
+ if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
+ return;
+ }
+ long duration = brOptions.getTemporaryAppAllowlistDuration();
+ final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
+ final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
+ final String reason = brOptions.getTemporaryAppAllowlistReason();
+
+ if (duration > Integer.MAX_VALUE) {
+ duration = Integer.MAX_VALUE;
+ }
+ // XXX ideally we should pause the broadcast until everything behind this is done,
+ // or else we will likely start dispatching the broadcast before we have opened
+ // access to the app (there is a lot of asynchronicity behind this). It is probably
+ // not that big a deal, however, because the main purpose here is to allow apps
+ // to hold wake locks, and they will be able to acquire their wake lock immediately
+ // it just won't be enabled until we get through this work.
+ StringBuilder b = new StringBuilder();
+ b.append("broadcast:");
+ UserHandle.formatUid(b, r.callingUid);
+ b.append(":");
+ if (r.intent.getAction() != null) {
+ b.append(r.intent.getAction());
+ } else if (r.intent.getComponent() != null) {
+ r.intent.getComponent().appendShortString(b);
+ } else if (r.intent.getData() != null) {
+ b.append(r.intent.getData());
+ }
+ b.append(",reason:");
+ b.append(reason);
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
+ + " type=" + type + " : " + b.toString());
+ }
+ mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
+ r.callingUid);
+ }
+
+ private void processNextBroadcast(boolean fromMsg) {
+ synchronized (mService) {
+ processNextBroadcastLocked(fromMsg, false);
+ }
+ }
+
+ private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
+ @Nullable Bundle filteredExtras) {
+ final Intent intent = new Intent(originalIntent);
+ if (filteredExtras != null) {
+ intent.replaceExtras(filteredExtras);
+ }
+ return intent;
+ }
+
+ public void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
+ BroadcastRecord r;
+
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ + mQueueName + "]: "
+ + mParallelBroadcasts.size() + " parallel broadcasts; "
+ + mDispatcher.describeStateLocked());
+
+ mService.updateCpuStats();
+
+ if (fromMsg) {
+ mBroadcastsScheduled = false;
+ }
+
+ // First, deliver any non-serialized broadcasts right away.
+ while (mParallelBroadcasts.size() > 0) {
+ r = mParallelBroadcasts.remove(0);
+ r.dispatchTime = SystemClock.uptimeMillis();
+ r.dispatchRealTime = SystemClock.elapsedRealtime();
+ r.dispatchClockTime = System.currentTimeMillis();
+ r.mIsReceiverAppRunning = true;
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
+ }
+
+ final int N = r.receivers.size();
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ + mQueueName + "] " + r);
+ for (int i=0; i<N; i++) {
+ Object target = r.receivers.get(i);
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Delivering non-ordered on [" + mQueueName + "] to registered "
+ + target + ": " + r);
+ deliverToRegisteredReceiverLocked(r,
+ (BroadcastFilter) target, false, i);
+ }
+ addBroadcastToHistoryLocked(r);
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ + mQueueName + "] " + r);
+ }
+
+ // Now take care of the next serialized one...
+
+ // If we are waiting for a process to come up to handle the next
+ // broadcast, then do nothing at this point. Just in case, we
+ // check that the process we're waiting for still exists.
+ if (mPendingBroadcast != null) {
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "processNextBroadcast [" + mQueueName + "]: waiting for "
+ + mPendingBroadcast.curApp);
+
+ boolean isDead;
+ if (mPendingBroadcast.curApp.getPid() > 0) {
+ synchronized (mService.mPidsSelfLocked) {
+ ProcessRecord proc = mService.mPidsSelfLocked.get(
+ mPendingBroadcast.curApp.getPid());
+ isDead = proc == null || proc.mErrorState.isCrashing();
+ }
+ } else {
+ final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
+ mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
+ isDead = proc == null || !proc.isPendingStart();
+ }
+ if (!isDead) {
+ // It's still alive, so keep waiting
+ return;
+ } else {
+ Slog.w(TAG, "pending app ["
+ + mQueueName + "]" + mPendingBroadcast.curApp
+ + " died before responding to broadcast");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ }
+ }
+
+ boolean looped = false;
+
+ do {
+ final long now = SystemClock.uptimeMillis();
+ r = mDispatcher.getNextBroadcastLocked(now);
+
+ if (r == null) {
+ // No more broadcasts are deliverable right now, so all done!
+ mDispatcher.scheduleDeferralCheckLocked(false);
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.scheduleAppGcsLPf();
+ }
+ if (looped && !skipOomAdj) {
+ // If we had finished the last ordered broadcast, then
+ // make sure all processes have correct oom and sched
+ // adjustments.
+ mService.updateOomAdjPendingTargetsLocked(
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+
+ // when we have no more ordered broadcast on this queue, stop logging
+ if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
+ mLogLatencyMetrics = false;
+ }
+
+ return;
+ }
+
+ boolean forceReceive = false;
+
+ // Ensure that even if something goes awry with the timeout
+ // detection, we catch "hung" broadcasts here, discard them,
+ // and continue to make progress.
+ //
+ // This is only done if the system is ready so that early-stage receivers
+ // don't get executed with timeouts; and of course other timeout-
+ // exempt broadcasts are ignored.
+ int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
+ if ((numReceivers > 0) &&
+ (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
+ Slog.w(TAG, "Hung broadcast ["
+ + mQueueName + "] discarded after timeout failure:"
+ + " now=" + now
+ + " dispatchTime=" + r.dispatchTime
+ + " startTime=" + r.receiverTime
+ + " intent=" + r.intent
+ + " numReceivers=" + numReceivers
+ + " nextReceiver=" + r.nextReceiver
+ + " state=" + r.state);
+ broadcastTimeoutLocked(false); // forcibly finish this broadcast
+ forceReceive = true;
+ r.state = BroadcastRecord.IDLE;
+ }
+ }
+
+ if (r.state != BroadcastRecord.IDLE) {
+ if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
+ "processNextBroadcast("
+ + mQueueName + ") called when not idle (state="
+ + r.state + ")");
+ return;
+ }
+
+ // Is the current broadcast is done for any reason?
+ if (r.receivers == null || r.nextReceiver >= numReceivers
+ || r.resultAbort || forceReceive) {
+ // Send the final result if requested
+ if (r.resultTo != null) {
+ boolean sendResult = true;
+
+ // if this was part of a split/deferral complex, update the refcount and only
+ // send the completion when we clear all of them
+ if (r.splitToken != 0) {
+ int newCount = mSplitRefcounts.get(r.splitToken) - 1;
+ if (newCount == 0) {
+ // done! clear out this record's bookkeeping and deliver
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Sending broadcast completion for split token "
+ + r.splitToken + " : " + r.intent.getAction());
+ }
+ mSplitRefcounts.delete(r.splitToken);
+ } else {
+ // still have some split broadcast records in flight; update refcount
+ // and hold off on the callback
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Result refcount now " + newCount + " for split token "
+ + r.splitToken + " : " + r.intent.getAction()
+ + " - not sending completion yet");
+ }
+ sendResult = false;
+ mSplitRefcounts.put(r.splitToken, newCount);
+ }
+ }
+ if (sendResult) {
+ if (r.callerApp != null) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+ r.callerApp, OOM_ADJ_REASON_FINISH_RECEIVER);
+ }
+ try {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
+ }
+ if (r.dispatchTime == 0) {
+ // The dispatch time here could be 0, in case it's a parallel
+ // broadcast but it has a result receiver. Set it to now.
+ r.dispatchTime = now;
+ }
+ r.mIsReceiverAppRunning = true;
+ performReceiveLocked(r.callerApp, r.resultTo,
+ new Intent(r.intent), r.resultCode,
+ r.resultData, r.resultExtras, false, false, r.userId,
+ r.callingUid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ now - r.dispatchTime);
+ logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+ // Set this to null so that the reference
+ // (local and remote) isn't kept in the mBroadcastHistory.
+ r.resultTo = null;
+ } catch (RemoteException e) {
+ r.resultTo = null;
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + r.intent, e);
+ }
+ }
+ }
+
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
+ cancelBroadcastTimeoutLocked();
+
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "Finished with ordered broadcast " + r);
+
+ // ... and on to the next...
+ addBroadcastToHistoryLocked(r);
+ if (r.intent.getComponent() == null && r.intent.getPackage() == null
+ && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ // This was an implicit broadcast... let's record it for posterity.
+ mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
+ r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
+ }
+ mDispatcher.retireBroadcastLocked(r);
+ r = null;
+ looped = true;
+ continue;
+ }
+
+ // Check whether the next receiver is under deferral policy, and handle that
+ // accordingly. If the current broadcast was already part of deferred-delivery
+ // tracking, we know that it must now be deliverable as-is without re-deferral.
+ if (!r.deferred) {
+ final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
+ if (mDispatcher.isDeferringLocked(receiverUid)) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
+ + " at " + r.nextReceiver + " is under deferral");
+ }
+ // If this is the only (remaining) receiver in the broadcast, "splitting"
+ // doesn't make sense -- just defer it as-is and retire it as the
+ // currently active outgoing broadcast.
+ BroadcastRecord defer;
+ if (r.nextReceiver + 1 == numReceivers) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Sole receiver of " + r
+ + " is under deferral; setting aside and proceeding");
+ }
+ defer = r;
+ mDispatcher.retireBroadcastLocked(r);
+ } else {
+ // Nontrivial case; split out 'uid's receivers to a new broadcast record
+ // and defer that, then loop and pick up continuing delivery of the current
+ // record (now absent those receivers).
+
+ // The split operation is guaranteed to match at least at 'nextReceiver'
+ defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Post split:");
+ Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
+ for (int i = 0; i < r.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
+ }
+ Slog.i(TAG_BROADCAST, "Split receivers:");
+ for (int i = 0; i < defer.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
+ }
+ }
+ // Track completion refcount as well if relevant
+ if (r.resultTo != null) {
+ int token = r.splitToken;
+ if (token == 0) {
+ // first split of this record; refcount for 'r' and 'deferred'
+ r.splitToken = defer.splitToken = nextSplitTokenLocked();
+ mSplitRefcounts.put(r.splitToken, 2);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Broadcast needs split refcount; using new token "
+ + r.splitToken);
+ }
+ } else {
+ // new split from an already-refcounted situation; increment count
+ final int curCount = mSplitRefcounts.get(token);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ if (curCount == 0) {
+ Slog.wtf(TAG_BROADCAST,
+ "Split refcount is zero with token for " + r);
+ }
+ }
+ mSplitRefcounts.put(token, curCount + 1);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "New split count for token " + token
+ + " is " + (curCount + 1));
+ }
+ }
+ }
+ }
+ mDispatcher.addDeferredBroadcast(receiverUid, defer);
+ r = null;
+ looped = true;
+ continue;
+ }
+ }
+ } while (r == null);
+
+ // Get the next receiver...
+ int recIdx = r.nextReceiver++;
+
+ // Keep track of when this receiver started, and make sure there
+ // is a timeout message pending to kill it if need be.
+ r.receiverTime = SystemClock.uptimeMillis();
+ if (recIdx == 0) {
+ r.dispatchTime = r.receiverTime;
+ r.dispatchRealTime = SystemClock.elapsedRealtime();
+ r.dispatchClockTime = System.currentTimeMillis();
+
+ if (mLogLatencyMetrics) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
+ r.dispatchClockTime - r.enqueueClockTime);
+ }
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
+ }
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ + mQueueName + "] " + r);
+ }
+ if (! mPendingBroadcastTimeoutMessage) {
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Submitting BROADCAST_TIMEOUT_MSG ["
+ + mQueueName + "] for " + r + " at " + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ }
+
+ final BroadcastOptions brOptions = r.options;
+ final Object nextReceiver = r.receivers.get(recIdx);
+
+ if (nextReceiver instanceof BroadcastFilter) {
+ // Simple case: this is a registered receiver who gets
+ // a direct call.
+ BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Delivering ordered ["
+ + mQueueName + "] to registered "
+ + filter + ": " + r);
+ r.mIsReceiverAppRunning = true;
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
+ if (r.receiver == null || !r.ordered) {
+ // The receiver has already finished, so schedule to
+ // process the next one.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ + mQueueName + "]: ordered="
+ + r.ordered + " receiver=" + r.receiver);
+ r.state = BroadcastRecord.IDLE;
+ scheduleBroadcastsLocked();
+ } else {
+ if (filter.receiverList != null) {
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ // r is guaranteed ordered at this point, so we know finishReceiverLocked()
+ // will get a callback and handle the activity start token lifecycle.
+ }
+ }
+ return;
+ }
+
+ // Hard case: need to instantiate the receiver, possibly
+ // starting its application process to host it.
+
+ final ResolveInfo info =
+ (ResolveInfo)nextReceiver;
+ final ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+
+ final String targetProcess = info.activityInfo.processName;
+ final ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+ info.activityInfo.applicationInfo.uid);
+
+ boolean skip = mSkipPolicy.shouldSkip(r, info);
+
+ // Filter packages in the intent extras, skipping delivery if none of the packages is
+ // visible to the receiver.
+ Bundle filteredExtras = null;
+ if (!skip && r.filterExtrasForReceiver != null) {
+ final Bundle extras = r.intent.getExtras();
+ if (extras != null) {
+ filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
+ if (filteredExtras == null) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Skipping delivery to "
+ + info.activityInfo.packageName + " / " + receiverUid
+ + " : receiver is filtered by the package visibility");
+ }
+ skip = true;
+ }
+ }
+ }
+
+ if (skip) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Skipping delivery of ordered [" + mQueueName + "] "
+ + r + " for reason described above");
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
+ r.receiver = null;
+ r.curFilter = null;
+ r.state = BroadcastRecord.IDLE;
+ r.manifestSkipCount++;
+ scheduleBroadcastsLocked();
+ return;
+ }
+ r.manifestCount++;
+
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
+ r.state = BroadcastRecord.APP_RECEIVE;
+ r.curComponent = component;
+ r.curReceiver = info.activityInfo;
+ r.curFilteredExtras = filteredExtras;
+ if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + receiverUid);
+ }
+ final boolean isActivityCapable =
+ (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
+ maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
+
+ // Report that a component is used for explicit broadcasts.
+ if (r.intent.getComponent() != null && r.curComponent != null
+ && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
+ mService.mUsageStatsService.reportEvent(
+ r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
+ }
+
+ // Broadcast is being executed, its package can't be stopped.
+ try {
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ r.curComponent.getPackageName(), false, r.userId);
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + r.curComponent.getPackageName() + ": " + e);
+ }
+
+ // Is this receiver's application already running?
+ if (app != null && app.getThread() != null && !app.isKilled()) {
+ try {
+ app.addPackage(info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
+ maybeAddAllowBackgroundActivityStartsToken(app, r);
+ r.mIsReceiverAppRunning = true;
+ processCurBroadcastLocked(r, app);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when sending broadcast to "
+ + r.curComponent, e);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Failed sending broadcast to "
+ + r.curComponent + " with " + r.intent, e);
+ // If some unexpected exception happened, just skip
+ // this broadcast. At this point we are not in the call
+ // from a client, so throwing an exception out from here
+ // will crash the entire system instead of just whoever
+ // sent the broadcast.
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ // We need to reset the state if we failed to start the receiver.
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ // Not running -- get it started, to be executed when the app comes up.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Need to start app ["
+ + mQueueName + "] " + targetProcess + " for broadcast " + r);
+ r.curApp = mService.startProcessLocked(targetProcess,
+ info.activityInfo.applicationInfo, true,
+ r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+ new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
+ r.intent.getAction(), getHostingRecordTriggerType(r)),
+ isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
+ if (r.curApp == null) {
+ // Ah, this recipient is unavailable. Finish it if necessary,
+ // and mark the broadcast record as ready for the next.
+ Slog.w(TAG, "Unable to launch app "
+ + info.activityInfo.applicationInfo.packageName + "/"
+ + receiverUid + " for broadcast "
+ + r.intent + ": process is bad");
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
+ mPendingBroadcast = r;
+ mPendingBroadcastRecvIndex = recIdx;
+ }
+
+ private String getHostingRecordTriggerType(BroadcastRecord r) {
+ if (r.alarm) {
+ return HostingRecord.TRIGGER_TYPE_ALARM;
+ } else if (r.pushMessage) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
+ } else if (r.pushMessageOverQuota) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
+ }
+ return HostingRecord.TRIGGER_TYPE_UNKNOWN;
+ }
+
+ @Nullable
+ private String getTargetPackage(BroadcastRecord r) {
+ if (r.intent == null) {
+ return null;
+ }
+ if (r.intent.getPackage() != null) {
+ return r.intent.getPackage();
+ } else if (r.intent.getComponent() != null) {
+ return r.intent.getComponent().getPackageName();
+ }
+ return null;
+ }
+
+ private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
+ // Only log after last receiver.
+ // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
+ // last BroadcastRecord of the split broadcast which has non-null resultTo.
+ final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (r.nextReceiver < numReceivers) {
+ return;
+ }
+ final String action = r.intent.getAction();
+ int event = 0;
+ if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
+ event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+ }
+ if (event != 0) {
+ final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
+ final int completeLatency = (int)
+ (SystemClock.uptimeMillis() - r.enqueueTime);
+ final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
+ final int completeRealLatency = (int)
+ (SystemClock.elapsedRealtime() - r.enqueueRealTime);
+ int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
+ // This method is called very infrequently, no performance issue we call
+ // LocalServices.getService() here.
+ final UserManagerInternal umInternal = LocalServices.getService(
+ UserManagerInternal.class);
+ final UserInfo userInfo = umInternal.getUserInfo(r.userId);
+ if (userInfo != null) {
+ userType = UserManager.getUserTypeForStatsd(userInfo.userType);
+ }
+ Slog.i(TAG_BROADCAST,
+ "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
+ + action
+ + " dispatchLatency:" + dispatchLatency
+ + " completeLatency:" + completeLatency
+ + " dispatchRealLatency:" + dispatchRealLatency
+ + " completeRealLatency:" + completeRealLatency
+ + " receiversSize:" + numReceivers
+ + " userId:" + r.userId
+ + " userType:" + (userInfo != null? userInfo.userType : null));
+ FrameworkStatsLog.write(
+ BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
+ event,
+ dispatchLatency,
+ completeLatency,
+ dispatchRealLatency,
+ completeRealLatency,
+ r.userId,
+ userType);
+ }
+ }
+
+ private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
+ if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
+ return;
+ }
+ final String targetPackage = getTargetPackage(r);
+ // Ignore non-explicit broadcasts
+ if (targetPackage == null) {
+ return;
+ }
+ getUsageStatsManagerInternal().reportBroadcastDispatched(
+ r.callingUid, targetPackage, UserHandle.of(r.userId),
+ r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
+ mService.getUidStateLocked(targetUid));
+ }
+
+ @NonNull
+ private UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ final UsageStatsManagerInternal usageStatsManagerInternal =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ return usageStatsManagerInternal;
+ }
+
+ private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
+ if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
+ return;
+ }
+ String msgToken = (proc.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then add the token
+ proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ }
+
+ final void setBroadcastTimeoutLocked(long timeoutTime) {
+ if (! mPendingBroadcastTimeoutMessage) {
+ Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
+ mHandler.sendMessageAtTime(msg, timeoutTime);
+ mPendingBroadcastTimeoutMessage = true;
+ }
+ }
+
+ final void cancelBroadcastTimeoutLocked() {
+ if (mPendingBroadcastTimeoutMessage) {
+ mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
+ mPendingBroadcastTimeoutMessage = false;
+ }
+ }
+
+ final void broadcastTimeoutLocked(boolean fromMsg) {
+ if (fromMsg) {
+ mPendingBroadcastTimeoutMessage = false;
+ }
+
+ if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
+ if (fromMsg) {
+ if (!mService.mProcessesReady) {
+ // Only process broadcast timeouts if the system is ready; some early
+ // broadcasts do heavy work setting up system facilities
+ return;
+ }
+
+ // If the broadcast is generally exempt from timeout tracking, we're done
+ if (r.timeoutExempt) {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ + r.intent.getAction());
+ }
+ return;
+ }
+
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
+ if (timeoutTime > now) {
+ // We can observe premature timeouts because we do not cancel and reset the
+ // broadcast timeout message after each receiver finishes. Instead, we set up
+ // an initial timeout then kick it down the road a little further as needed
+ // when it expires.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Premature timeout ["
+ + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ return;
+ }
+ }
+
+ if (r.state == BroadcastRecord.WAITING_SERVICES) {
+ // In this case the broadcast had already finished, but we had decided to wait
+ // for started services to finish as well before going on. So if we have actually
+ // waited long enough time timeout the broadcast, let's give up on the whole thing
+ // and just move on to the next.
+ Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
+ ? r.curComponent.flattenToShortString() : "(null)"));
+ r.curComponent = null;
+ r.state = BroadcastRecord.IDLE;
+ processNextBroadcastLocked(false, false);
+ return;
+ }
+
+ // If the receiver app is being debugged we quietly ignore unresponsiveness, just
+ // tidying up and moving on to the next broadcast without crashing or ANRing this
+ // app just because it's stopped at a breakpoint.
+ final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
+
+ long timeoutDurationMs = now - r.receiverTime;
+ Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ + ", started " + timeoutDurationMs + "ms ago");
+ r.receiverTime = now;
+ if (!debugging) {
+ r.anrCount++;
+ }
+
+ ProcessRecord app = null;
+ TimeoutRecord timeoutRecord = null;
+
+ Object curReceiver;
+ if (r.nextReceiver > 0) {
+ curReceiver = r.receivers.get(r.nextReceiver-1);
+ r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
+ } else {
+ curReceiver = r.curReceiver;
+ }
+ Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
+ logBroadcastReceiverDiscardLocked(r);
+ if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter)curReceiver;
+ if (bf.receiverList.pid != 0
+ && bf.receiverList.pid != ActivityManagerService.MY_PID) {
+ synchronized (mService.mPidsSelfLocked) {
+ app = mService.mPidsSelfLocked.get(
+ bf.receiverList.pid);
+ }
+ }
+ } else {
+ app = r.curApp;
+ }
+
+ if (app != null) {
+ String anrMessage =
+ "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
+ + "ms";
+ timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
+ }
+
+ if (mPendingBroadcast == r) {
+ mPendingBroadcast = null;
+ }
+
+ // Move on to the next receiver.
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+
+ if (!debugging && timeoutRecord != null) {
+ mService.mAnrHelper.appNotResponding(app, timeoutRecord);
+ }
+ }
+
+ private final int ringAdvance(int x, final int increment, final int ringSize) {
+ x += increment;
+ if (x < 0) return (ringSize - 1);
+ else if (x >= ringSize) return 0;
+ else return x;
+ }
+
+ private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
+ if (original.callingUid < 0) {
+ // This was from a registerReceiver() call; ignore it.
+ return;
+ }
+ original.finishTime = SystemClock.uptimeMillis();
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(original));
+ }
+
+ final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
+ final String callerPackage = info != null ? info.packageName : original.callerPackage;
+ if (callerPackage != null) {
+ mService.mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
+ original.callingUid, 0, callerPackage).sendToTarget();
+ }
+
+ // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
+ // So don't change the incoming record directly.
+ final BroadcastRecord historyRecord = original.maybeStripForHistory();
+
+ mBroadcastHistory[mHistoryNext] = historyRecord;
+ mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
+
+ mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
+ mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
+ mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
+ mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
+ mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
+ }
+
+ public boolean cleanupDisabledPackageReceiversLocked(
+ String packageName, Set<String> filterByClasses, int userId, boolean doit) {
+ boolean didSomething = false;
+ for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
+ didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
+ packageName, filterByClasses, userId, doit);
+ if (!doit && didSomething) {
+ return true;
+ }
+ }
+
+ didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
+ filterByClasses, userId, doit);
+
+ return didSomething;
+ }
+
+ final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
+ final int logIndex = r.nextReceiver - 1;
+ if (logIndex >= 0 && logIndex < r.receivers.size()) {
+ Object curReceiver = r.receivers.get(logIndex);
+ if (curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter) curReceiver;
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
+ bf.owningUserId, System.identityHashCode(r),
+ r.intent.getAction(), logIndex, System.identityHashCode(bf));
+ } else {
+ ResolveInfo ri = (ResolveInfo) curReceiver;
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
+ System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
+ }
+ } else {
+ if (logIndex < 0) Slog.w(TAG,
+ "Discarding broadcast before first receiver is invoked: " + r);
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ -1, System.identityHashCode(r),
+ r.intent.getAction(),
+ r.nextReceiver,
+ "NONE");
+ }
+ }
+
+ private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
+ return formatSimple("Broadcast %s from %s (%s) %s",
+ state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
+ record.callerPackage == null ? "" : record.callerPackage,
+ record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
+ record.intent == null ? "" : record.intent.getAction());
+ }
+
+ public boolean isIdle() {
+ return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
+ && (mPendingBroadcast == null);
+ }
+
+ public void flush() {
+ cancelDeferrals();
+ }
+
+ // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
+ // be immediately deliverable.
+ public void cancelDeferrals() {
+ synchronized (mService) {
+ mDispatcher.cancelDeferralsLocked();
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ public String describeState() {
+ synchronized (mService) {
+ return mParallelBroadcasts.size() + " parallel; "
+ + mDispatcher.describeStateLocked();
+ }
+ }
+
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
+ int N;
+ N = mParallelBroadcasts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
+ }
+ mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
+ if (mPendingBroadcast != null) {
+ mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
+ }
+
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r != null) {
+ r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
+ }
+ } while (ringIndex != lastIndex);
+
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
+ intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
+ false, true, true, false);
+ proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
+ mSummaryHistoryEnqueueTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
+ mSummaryHistoryDispatchTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
+ mSummaryHistoryFinishTime[ringIndex]);
+ proto.end(summaryToken);
+ } while (ringIndex != lastIndex);
+ proto.end(token);
+ }
+
+ public boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
+ || mPendingBroadcast != null) {
+ boolean printed = false;
+ for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
+ BroadcastRecord br = mParallelBroadcasts.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ printed = true;
+ pw.println(" Active broadcasts [" + mQueueName + "]:");
+ }
+ pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
+ br.dump(pw, " ", sdf);
+ }
+
+ mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
+
+ if (dumpPackage == null || (mPendingBroadcast != null
+ && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
+ pw.println();
+ pw.println(" Pending broadcast [" + mQueueName + "]:");
+ if (mPendingBroadcast != null) {
+ mPendingBroadcast.dump(pw, " ", sdf);
+ } else {
+ pw.println(" (null)");
+ }
+ needSep = true;
+ }
+ }
+
+ mConstants.dump(pw);
+
+ int i;
+ boolean printed = false;
+
+ i = -1;
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+
+ i++; // genuine record of some sort even if we're filtering it out
+ if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (dumpAll) {
+ pw.print(" Historical Broadcast " + mQueueName + " #");
+ pw.print(i); pw.println(":");
+ r.dump(pw, " ", sdf);
+ } else {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ pw.print(" ");
+ pw.println(r.intent.toShortString(false, true, true, false));
+ if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
+ pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
+ }
+ Bundle bundle = r.intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ }
+ } while (ringIndex != lastIndex);
+
+ if (dumpPackage == null) {
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ if (dumpAll) {
+ printed = false;
+ i = -1;
+ } else {
+ // roll over the 'i' full dumps that have already been issued
+ for (int j = i;
+ j > 0 && ringIndex != lastIndex;) {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+ j--;
+ }
+ }
+ // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
+ // the overall broadcast history.
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (!dumpAll && i >= 50) {
+ pw.println(" ...");
+ break;
+ }
+ i++;
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(intent.toShortString(false, true, true, false));
+ pw.print(" ");
+ TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+ - mSummaryHistoryEnqueueTime[ringIndex], pw);
+ pw.print(" dispatch ");
+ TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+ - mSummaryHistoryDispatchTime[ringIndex], pw);
+ pw.println(" finish");
+ pw.print(" enq=");
+ pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+ pw.print(" disp=");
+ pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+ pw.print(" fin=");
+ pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ } while (ringIndex != lastIndex);
+ }
+
+ return needSep;
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
new file mode 100644
index 000000000000..9569848cbe37
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerService.checkComponentPermission;
+import static com.android.server.am.BroadcastQueue.TAG;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.permission.IPermissionManager;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Policy logic that decides if delivery of a particular {@link BroadcastRecord}
+ * should be skipped for a given {@link ResolveInfo} or {@link BroadcastFilter}.
+ * <p>
+ * This policy should be consulted as close as possible to the actual dispatch.
+ */
+public class BroadcastSkipPolicy {
+ private final ActivityManagerService mService;
+
+ public BroadcastSkipPolicy(ActivityManagerService service) {
+ mService = service;
+ }
+
+ /**
+ * Determine if the given {@link BroadcastRecord} is eligible to be sent to
+ * the given {@link ResolveInfo}.
+ */
+ public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull ResolveInfo info) {
+ final BroadcastOptions brOptions = r.options;
+ final ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
+
+ if (brOptions != null &&
+ (info.activityInfo.applicationInfo.targetSdkVersion
+ < brOptions.getMinManifestReceiverApiLevel() ||
+ info.activityInfo.applicationInfo.targetSdkVersion
+ > brOptions.getMaxManifestReceiverApiLevel())) {
+ Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo
+ + " targets " + info.activityInfo.applicationInfo.targetSdkVersion
+ + " but delivery restricted to ["
+ + brOptions.getMinManifestReceiverApiLevel() + ", "
+ + brOptions.getMaxManifestReceiverApiLevel()
+ + "] broadcasting " + broadcastDescription(r, component));
+ return true;
+ }
+ if (brOptions != null &&
+ !brOptions.testRequireCompatChange(info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Compat change filtered: broadcasting " + broadcastDescription(r, component)
+ + " to uid " + info.activityInfo.applicationInfo.uid + " due to compat change "
+ + r.options.getRequireCompatChangeId());
+ return true;
+ }
+ if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + broadcastDescription(r, component));
+ return true;
+ }
+ if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + broadcastDescription(r, component));
+ return true;
+ }
+ int perm = checkComponentPermission(info.activityInfo.permission,
+ r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+ info.activityInfo.exported);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (!info.activityInfo.exported) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid);
+ } else {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " requires " + info.activityInfo.permission);
+ }
+ return true;
+ } else if (info.activityInfo.permission != null) {
+ final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
+ if (opCode != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(opCode,
+ r.callingUid, r.callerPackage, r.callerFeatureId,
+ "Broadcast delivered to " + info.activityInfo.name)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " requires appop " + AppOpsManager.permissionToOp(
+ info.activityInfo.permission));
+ return true;
+ }
+ }
+
+ boolean isSingleton = false;
+ try {
+ isSingleton = mService.isSingleton(info.activityInfo.processName,
+ info.activityInfo.applicationInfo,
+ info.activityInfo.name, info.activityInfo.flags);
+ } catch (SecurityException e) {
+ Slog.w(TAG, e.getMessage());
+ return true;
+ }
+ if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ info.activityInfo.applicationInfo.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS);
+ return true;
+ }
+ }
+ if (info.activityInfo.applicationInfo.isInstantApp()
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " Instant Apps do not support manifest receivers");
+ return true;
+ }
+ if (r.callerInstantApp
+ && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " requires receiver have visibleToInstantApps set"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
+ // If the target process is crashing, just skip it.
+ Slog.w(TAG, "Skipping deliver ordered [" + r.queue.toString() + "] " + r
+ + " to " + r.curApp + ": process crashing");
+ return true;
+ }
+
+ boolean isAvailable = false;
+ try {
+ isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
+ info.activityInfo.packageName,
+ UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (Exception e) {
+ // all such failures mean we skip this receiver
+ Slog.w(TAG, "Exception getting recipient info for "
+ + info.activityInfo.packageName, e);
+ }
+ if (!isAvailable) {
+ Slog.w(TAG,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid
+ + " : package no longer available");
+ return true;
+ }
+
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
+ info.activityInfo.packageName, UserHandle.getUserId(
+ info.activityInfo.applicationInfo.uid))) {
+ Slog.w(TAG,
+ "Skipping delivery: permission review required for "
+ + broadcastDescription(r, component));
+ return true;
+ }
+
+ // This is safe to do even if we are skipping the broadcast, and we need
+ // this information now to evaluate whether it is going to be allowed to run.
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+ // If it's a singleton, it needs to be the same app or a special app
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton
+ && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
+ }
+
+ final int allowed = mService.getAppStartModeLOSP(
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+ // We won't allow this receiver to be launched if the app has been
+ // completely disabled from launches, or it was not explicitly sent
+ // to it and the app is in a state that should not receive it
+ // (depending on how getAppStartModeLOSP has determined that).
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Background execution disabled: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ return true;
+ } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
+ || (r.intent.getComponent() == null
+ && r.intent.getPackage() == null
+ && ((r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
+ && !isSignaturePerm(r.requiredPermissions))) {
+ mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
+ component.getPackageName());
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ return true;
+ }
+ }
+
+ if (!Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
+ && !mService.mUserController
+ .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
+ 0 /* flags */)) {
+ Slog.w(TAG,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid + " : user is not running");
+ return true;
+ }
+
+ if (r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager()
+ .checkPermission(excludedPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ // When there is an app op associated with the permission,
+ // skip when both the permission and the app op are
+ // granted.
+ if ((perm == PackageManager.PERMISSION_GRANTED) && (
+ mService.getAppOpsManager().checkOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName)
+ == AppOpsManager.MODE_ALLOWED)) {
+ return true;
+ }
+ } else {
+ // When there is no app op associated with the permission,
+ // skip when permission is granted.
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // Check that the receiver does *not* belong to any of the excluded packages
+ if (r.excludedPackages != null && r.excludedPackages.length > 0) {
+ if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) {
+ Slog.w(TAG, "Skipping delivery of excluded package "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " excludes package " + component.getPackageName()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ }
+
+ if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+ r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ for (int i = 0; i < r.requiredPermissions.length; i++) {
+ String requiredPermission = r.requiredPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager().
+ checkPermission(requiredPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires " + requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
+ if (!noteOpForManifestReceiver(appOp, r, info, component)) {
+ return true;
+ }
+ }
+ }
+ }
+ if (r.appOp != AppOpsManager.OP_NONE) {
+ if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if the given {@link BroadcastRecord} is eligible to be sent to
+ * the given {@link BroadcastFilter}.
+ */
+ public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull BroadcastFilter filter) {
+ if (r.options != null && !r.options.testRequireCompatChange(filter.owningUid)) {
+ Slog.w(TAG, "Compat change filtered: broadcasting " + r.intent.toString()
+ + " to uid " + filter.owningUid + " due to compat change "
+ + r.options.getRequireCompatChangeId());
+ return true;
+ }
+ if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ filter.packageName, filter.owningUid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ return true;
+ }
+ if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, filter.receiverList.uid)) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ return true;
+ }
+ // Check that the sender has permission to send to this receiver
+ if (filter.requiredPermission != null) {
+ int perm = checkComponentPermission(filter.requiredPermission,
+ r.callingPid, r.callingUid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " requires " + filter.requiredPermission
+ + " due to registered receiver " + filter);
+ return true;
+ } else {
+ final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
+ if (opCode != AppOpsManager.OP_NONE
+ && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
+ r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(
+ filter.requiredPermission)
+ + " due to registered receiver " + filter);
+ return true;
+ }
+ }
+ }
+
+ if ((filter.receiverList.app == null || filter.receiverList.app.isKilled()
+ || filter.receiverList.app.mErrorState.isCrashing())) {
+ Slog.w(TAG, "Skipping deliver [" + r.queue.toString() + "] " + r
+ + " to " + filter.receiverList + ": process gone or crashing");
+ return true;
+ }
+
+ // Ensure that broadcasts are only sent to other Instant Apps if they are marked as
+ // visible to Instant Apps.
+ final boolean visibleToInstantApps =
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+
+ if (!visibleToInstantApps && filter.instantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
+ return true;
+ }
+
+ if (!filter.visibleToInstantApp && r.callerInstantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires receiver be visible to instant apps"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+
+ // Check that the receiver has the required permission(s) to receive this broadcast.
+ if (r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ for (int i = 0; i < r.requiredPermissions.length; i++) {
+ String requiredPermission = r.requiredPermissions[i];
+ int perm = checkComponentPermission(requiredPermission,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires " + requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
+ && mService.getAppOpsManager().noteOpNoThrow(appOp,
+ filter.receiverList.uid, filter.packageName, filter.featureId,
+ "Broadcast delivered to registered receiver " + filter.receiverId)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(
+ requiredPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ }
+ }
+ if ((r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
+ int perm = checkComponentPermission(null,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: security check failed when receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ }
+ // Check that the receiver does *not* have any excluded permissions
+ if (r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ final int perm = checkComponentPermission(excludedPermission,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ // When there is an app op associated with the permission,
+ // skip when both the permission and the app op are
+ // granted.
+ if ((perm == PackageManager.PERMISSION_GRANTED) && (
+ mService.getAppOpsManager().checkOpNoThrow(appOp,
+ filter.receiverList.uid,
+ filter.packageName)
+ == AppOpsManager.MODE_ALLOWED)) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes appop " + AppOpsManager.permissionToOp(
+ excludedPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ } else {
+ // When there is no app op associated with the permission,
+ // skip when permission is granted.
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes " + excludedPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ }
+ }
+ }
+
+ // Check that the receiver does *not* belong to any of the excluded packages
+ if (r.excludedPackages != null && r.excludedPackages.length > 0) {
+ if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) {
+ Slog.w(TAG, "Skipping delivery of excluded package "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes package " + filter.packageName
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+ }
+
+ // If the broadcast also requires an app op check that as well.
+ if (r.appOp != AppOpsManager.OP_NONE
+ && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+ filter.receiverList.uid, filter.packageName, filter.featureId,
+ "Broadcast delivered to registered receiver " + filter.receiverId)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires appop " + AppOpsManager.opToName(r.appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return true;
+ }
+
+ // Ensure that broadcasts are only sent to other apps if they are explicitly marked as
+ // exported, or are System level broadcasts
+ if (!filter.exported && checkComponentPermission(null, r.callingPid,
+ r.callingUid, filter.receiverList.uid, filter.exported)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Exported Denial: sending "
+ + r.intent.toString()
+ + ", action: " + r.intent.getAction()
+ + " from " + r.callerPackage
+ + " (uid=" + r.callingUid + ")"
+ + " due to receiver " + filter.receiverList.app
+ + " (uid " + filter.receiverList.uid + ")"
+ + " not specifying RECEIVER_EXPORTED");
+ return true;
+ }
+
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+ filter.owningUserId)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static String broadcastDescription(BroadcastRecord r, ComponentName component) {
+ return r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
+ }
+
+ private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component) {
+ if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {
+ return noteOpForManifestReceiverInner(appOp, r, info, component, null);
+ } else {
+ // Attribution tags provided, noteOp each tag
+ for (String tag : info.activityInfo.attributionTags) {
+ if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component, String tag) {
+ if (mService.getAppOpsManager().noteOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName,
+ tag,
+ "Broadcast delivered to " + info.activityInfo.name)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires appop " + AppOpsManager.opToName(appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return true if all given permissions are signature-only perms.
+ */
+ private boolean isSignaturePerm(String[] perms) {
+ if (perms == null) {
+ return false;
+ }
+ IPermissionManager pm = AppGlobals.getPermissionManager();
+ for (int i = perms.length-1; i >= 0; i--) {
+ try {
+ PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
+ if (pi == null) {
+ // a required permission that no package has actually
+ // defined cannot be signature-required.
+ return false;
+ }
+ if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
+ | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
+ != PermissionInfo.PROTECTION_SIGNATURE) {
+ // If this a signature permission and NOT allowed for privileged apps, it
+ // is okay... otherwise, nope!
+ return false;
+ }
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean requestStartTargetPermissionsReviewIfNeededLocked(
+ BroadcastRecord receiverRecord, String receivingPackageName,
+ final int receivingUserId) {
+ if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
+ receivingPackageName, receivingUserId)) {
+ return true;
+ }
+
+ final boolean callerForeground = receiverRecord.callerApp != null
+ ? receiverRecord.callerApp.mState.getSetSchedGroup()
+ != ProcessList.SCHED_GROUP_BACKGROUND : true;
+
+ // Show a permission review UI only for explicit broadcast from a foreground app
+ if (callerForeground && receiverRecord.intent.getComponent() != null) {
+ IIntentSender target = mService.mPendingIntentController.getIntentSender(
+ ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
+ receiverRecord.callerFeatureId, receiverRecord.callingUid,
+ receiverRecord.userId, null, null, 0,
+ new Intent[]{receiverRecord.intent},
+ new String[]{receiverRecord.intent.resolveType(mService.mContext
+ .getContentResolver())},
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null);
+
+ final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName);
+ intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+
+ if (DEBUG_PERMISSIONS_REVIEW) {
+ Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package "
+ + receivingPackageName);
+ }
+
+ mService.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId));
+ }
+ });
+ } else {
+ Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package"
+ + receivingPackageName + " requires a permissions review");
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 12aa66b84d85..de28be009c24 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
-import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
@@ -2566,16 +2565,10 @@ public class OomAdjuster {
case PROCESS_STATE_BOUND_TOP:
return PROCESS_CAPABILITY_NETWORK;
case PROCESS_STATE_FOREGROUND_SERVICE:
- if (psr.hasForegroundServices()) {
- // Capability from FGS are conditional depending on foreground service type in
- // manifest file and the mAllowWhileInUsePermissionInFgs flag.
- return PROCESS_CAPABILITY_NETWORK;
- } else {
- // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client.
- // the implicit capability could be removed in the future, client should use
- // BIND_INCLUDE_CAPABILITY flag.
- return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK;
- }
+ // Capability from foreground service is conditional depending on
+ // foregroundServiceType in the manifest file and the
+ // mAllowWhileInUsePermissionInFgs flag.
+ return PROCESS_CAPABILITY_NETWORK;
case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
return PROCESS_CAPABILITY_NETWORK;
default:
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 134e2061c090..1302e226eba3 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -502,14 +502,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
GamePackageConfiguration(PackageManager packageManager, String packageName, int userId) {
mPackageName = packageName;
-
- // set flag default values
- mPerfModeOptedIn = false;
- mBatteryModeOptedIn = false;
- mAllowDownscale = true;
- mAllowAngle = true;
- mAllowFpsOverride = true;
-
try {
final ApplicationInfo ai = packageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
@@ -519,6 +511,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
+ } else {
+ mPerfModeOptedIn = false;
+ mBatteryModeOptedIn = false;
+ mAllowDownscale = true;
+ mAllowAngle = true;
+ mAllowFpsOverride = true;
}
}
} catch (NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index eba9c7a5f14e..e97d9c22620e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1771,8 +1771,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
return;
}
Log.w(TAG, "Communication client died");
- removeCommunicationRouteClient(client.getBinder(), true);
- onUpdateCommunicationRouteClient("onCommunicationRouteClientDied");
+ setCommunicationRouteForClient(client.getBinder(), client.getPid(), null,
+ BtHelper.SCO_MODE_UNDEFINED, "onCommunicationRouteClientDied");
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index f8543162f95e..2263e80039bd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -28,6 +28,7 @@ import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.IBiometricStateListener;
import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
@@ -45,7 +46,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
* @param <P> internal property type
*/
public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
- P extends SensorPropertiesInternal> implements ClientMonitorCallback {
+ P extends SensorPropertiesInternal>
+ implements ClientMonitorCallback, IBinder.DeathRecipient {
private static final String TAG = "BiometricStateCallback";
@@ -161,6 +163,11 @@ public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
@NonNull IBiometricStateListener listener) {
mBiometricStateListeners.add(listener);
broadcastCurrentEnrollmentState(listener);
+ try {
+ listener.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
}
private synchronized void broadcastCurrentEnrollmentState(
@@ -196,4 +203,19 @@ public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
Slog.e(TAG, "Remote exception", e);
}
}
-}
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ Slog.w(TAG, "Callback binder died: " + who);
+ if (mBiometricStateListeners.removeIf(listener -> listener.asBinder().equals(who))) {
+ Slog.w(TAG, "Removed dead listener for " + who);
+ } else {
+ Slog.w(TAG, "No dead listeners found");
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java
new file mode 100644
index 000000000000..48112c452f02
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioEventLogger.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.Slog;
+
+final class RadioEventLogger {
+ private final String mTag;
+ private final LocalLog mEventLogger;
+
+ RadioEventLogger(String tag, int loggerQueueSize) {
+ mTag = tag;
+ mEventLogger = new LocalLog(loggerQueueSize);
+ }
+
+ void logRadioEvent(String logFormat, Object... args) {
+ String log = String.format(logFormat, args);
+ mEventLogger.log(log);
+ if (Log.isLoggable(mTag, Log.DEBUG)) {
+ Slog.d(mTag, log);
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ mEventLogger.dump(pw);
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 852aa66866f0..0a23e385d67a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -55,12 +55,14 @@ import java.util.stream.Collectors;
class RadioModule {
private static final String TAG = "BcRadio2Srv.module";
+ private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
@NonNull private final IBroadcastRadio mService;
@NonNull public final RadioManager.ModuleProperties mProperties;
private final Object mLock;
@NonNull private final Handler mHandler;
+ @NonNull private final RadioEventLogger mEventLogger;
@GuardedBy("mLock")
private ITunerSession mHalTunerSession;
@@ -138,6 +140,7 @@ class RadioModule {
mService = Objects.requireNonNull(service);
mLock = Objects.requireNonNull(lock);
mHandler = new Handler(Looper.getMainLooper());
+ mEventLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
@@ -176,13 +179,14 @@ class RadioModule {
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
- Slog.i(TAG, "Open TunerSession");
+ mEventLogger.logRadioEvent("Open TunerSession");
synchronized (mLock) {
if (mHalTunerSession == null) {
Mutable<ITunerSession> hwSession = new Mutable<>();
mService.openSession(mHalTunerCallback, (result, session) -> {
Convert.throwOnError("openSession", result);
hwSession.value = session;
+ mEventLogger.logRadioEvent("New HIDL 2.0 tuner session is opened");
});
mHalTunerSession = Objects.requireNonNull(hwSession.value);
}
@@ -207,7 +211,7 @@ class RadioModule {
// Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
// must be called without mAidlTunerSessions locked because it can call
// onTunerSessionClosed().
- Slog.i(TAG, "Close TunerSessions");
+ mEventLogger.logRadioEvent("Close TunerSessions");
TunerSession[] tunerSessions;
synchronized (mLock) {
tunerSessions = new TunerSession[mAidlTunerSessions.size()];
@@ -320,7 +324,7 @@ class RadioModule {
}
onTunerSessionProgramListFilterChanged(null);
if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
- Slog.i(TAG, "Closing HAL tuner session");
+ mEventLogger.logRadioEvent("Closing HAL tuner session");
try {
mHalTunerSession.close();
} catch (RemoteException ex) {
@@ -372,7 +376,7 @@ class RadioModule {
public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
- Slog.i(TAG, "Add AnnouncementListener");
+ mEventLogger.logRadioEvent("Add AnnouncementListener");
ArrayList<Byte> enabledList = new ArrayList<>();
for (int type : enabledTypes) {
enabledList.add((byte)type);
@@ -409,7 +413,7 @@ class RadioModule {
}
Bitmap getImage(int id) {
- Slog.i(TAG, "Get image for id " + id);
+ mEventLogger.logRadioEvent("Get image for id %d", id);
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
@@ -449,6 +453,10 @@ class RadioModule {
}
pw.decreaseIndent();
}
+ pw.printf("Radio module events:\n");
+ pw.increaseIndent();
+ mEventLogger.dump(pw);
+ pw.decreaseIndent();
pw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 41f753c11216..918dc98e3a9e 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -28,7 +28,6 @@ import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Slog;
@@ -41,8 +40,10 @@ import java.util.Objects;
class TunerSession extends ITuner.Stub {
private static final String TAG = "BcRadio2Srv.session";
private static final String kAudioDeviceName = "Radio tuner source";
+ private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
private final Object mLock;
+ @NonNull private final RadioEventLogger mEventLogger;
private final RadioModule mModule;
private final ITunerSession mHwSession;
@@ -61,15 +62,12 @@ class TunerSession extends ITuner.Stub {
mHwSession = Objects.requireNonNull(hwSession);
mCallback = Objects.requireNonNull(callback);
mLock = Objects.requireNonNull(lock);
- }
-
- private boolean isDebugEnabled() {
- return Log.isLoggable(TAG, Log.DEBUG);
+ mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@Override
public void close() {
- if (isDebugEnabled()) Slog.d(TAG, "Close");
+ mEventLogger.logRadioEvent("Close");
close(null);
}
@@ -81,7 +79,7 @@ class TunerSession extends ITuner.Stub {
* @param error Optional error to send to client before session is closed.
*/
public void close(@Nullable Integer error) {
- if (isDebugEnabled()) Slog.d(TAG, "Close on error " + error);
+ mEventLogger.logRadioEvent("Close on error %d", error);
synchronized (mLock) {
if (mIsClosed) return;
if (error != null) {
@@ -145,10 +143,8 @@ class TunerSession extends ITuner.Stub {
@Override
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
- if (isDebugEnabled()) {
- Slog.d(TAG, "Step with directionDown " + directionDown
- + " skipSubChannel " + skipSubChannel);
- }
+ mEventLogger.logRadioEvent("Step with direction %s, skipSubChannel? %s",
+ directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.step(!directionDown);
@@ -158,10 +154,8 @@ class TunerSession extends ITuner.Stub {
@Override
public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
- if (isDebugEnabled()) {
- Slog.d(TAG, "Scan with directionDown " + directionDown
- + " skipSubChannel " + skipSubChannel);
- }
+ mEventLogger.logRadioEvent("Scan with direction %s, skipSubChannel? %s",
+ directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.scan(!directionDown, skipSubChannel);
@@ -171,7 +165,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void tune(ProgramSelector selector) throws RemoteException {
- if (isDebugEnabled()) Slog.d(TAG, "Tune with selector " + selector);
+ mEventLogger.logRadioEvent("Tune with selector %s", selector);
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
@@ -195,7 +189,7 @@ class TunerSession extends ITuner.Stub {
@Override
public Bitmap getImage(int id) {
- if (isDebugEnabled()) Slog.d(TAG, "Get image for " + id);
+ mEventLogger.logRadioEvent("Get image for %d", id);
return mModule.getImage(id);
}
@@ -208,7 +202,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
- if (isDebugEnabled()) Slog.d(TAG, "start programList updates " + filter);
+ mEventLogger.logRadioEvent("start programList updates %s", filter);
// If the AIDL client provides a null filter, it wants all updates, so use the most broad
// filter.
if (filter == null) {
@@ -267,7 +261,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void stopProgramListUpdates() throws RemoteException {
- if (isDebugEnabled()) Slog.d(TAG, "Stop programList updates");
+ mEventLogger.logRadioEvent("Stop programList updates");
synchronized (mLock) {
checkNotClosedLocked();
mProgramInfoCache = null;
@@ -291,7 +285,7 @@ class TunerSession extends ITuner.Stub {
@Override
public boolean isConfigFlagSet(int flag) {
- if (isDebugEnabled()) Slog.d(TAG, "Is ConfigFlagSet for " + ConfigFlag.toString(flag));
+ mEventLogger.logRadioEvent("Is ConfigFlagSet for %s", ConfigFlag.toString(flag));
synchronized (mLock) {
checkNotClosedLocked();
@@ -313,9 +307,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void setConfigFlag(int flag, boolean value) throws RemoteException {
- if (isDebugEnabled()) {
- Slog.d(TAG, "Set ConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
- }
+ mEventLogger.logRadioEvent("Set ConfigFlag %s = %b", ConfigFlag.toString(flag), value);
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.setConfigFlag(flag, value);
@@ -351,6 +343,10 @@ class TunerSession extends ITuner.Stub {
pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
pw.printf("Config: %s\n", mDummyConfig);
}
+ pw.printf("Tuner session events:\n");
+ pw.increaseIndent();
+ mEventLogger.dump(pw);
+ pw.decreaseIndent();
pw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 66ef5e761194..11eb78277beb 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -582,13 +582,18 @@ public class CameraServiceProxy extends SystemService
}
@Override
- public boolean isCameraDisabled() {
+ public boolean isCameraDisabled(int userId) {
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
Slog.e(TAG, "Failed to get the device policy manager service");
return false;
}
- return dpm.getCameraDisabled(null);
+ try {
+ return dpm.getCameraDisabled(null, userId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
}
};
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 931c692b5f01..b4e91b586b40 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1110,7 +1110,7 @@ public class Vpn {
// Except for Settings and VpnDialogs, the caller should be matched one of oldPackage or
// newPackage. Otherwise, non VPN owner might get the VPN always-on status of the VPN owner.
// See b/191382886.
- if (!hasControlVpnPermission()) {
+ if (mContext.checkCallingOrSelfPermission(CONTROL_VPN) != PERMISSION_GRANTED) {
if (oldPackage != null) {
verifyCallingUidAndPackage(oldPackage);
}
@@ -2073,10 +2073,6 @@ public class Vpn {
"Unauthorized Caller");
}
- private boolean hasControlVpnPermission() {
- return mContext.checkCallingOrSelfPermission(CONTROL_VPN) == PERMISSION_GRANTED;
- }
-
private class Connection implements ServiceConnection {
private IBinder mService;
@@ -3901,10 +3897,8 @@ public class Vpn {
Binder.restoreCallingIdentity(token);
}
- // If package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
- if (hasControlVpnPermission()) {
- setPackageAuthorization(packageName, VpnManager.TYPE_VPN_PLATFORM);
- }
+ // TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
+ // This mirrors the prepareAndAuthorize that is used by VpnService.
// Return whether the app is already pre-consented
return isVpnProfilePreConsented(mContext, packageName);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ea54b3001163..e6c2e7cae23e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -544,6 +544,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
|| message.getOpcode() == Constants.MESSAGE_SET_STREAM_PATH
|| message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
removeAction(ActiveSourceAction.class);
+ removeAction(OneTouchPlayAction.class);
return;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fa8b5c1b8086..3ee35036e9fd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1051,13 +1051,13 @@ public class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
- mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call
// onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
+ mAddressAllocated = true;
notifyAddressAllocated(allocatedDevices, initiatedBy);
// Reinvoke the saved display status callback once the local
// device is ready.
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index eb73234549c9..3e397468aa87 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -109,13 +109,11 @@ final class IInputMethodInvoker {
@AnyThread
void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations,
- int configChanges, boolean stylusHandWritingSupported,
- @InputMethodNavButtonFlags int navigationBarFlags) {
+ int configChanges, @InputMethodNavButtonFlags int navigationBarFlags) {
final IInputMethod.InitParams params = new IInputMethod.InitParams();
params.token = token;
params.privilegedOperations = privilegedOperations;
params.configChanges = configChanges;
- params.stylusHandWritingSupported = stylusHandWritingSupported;
params.navigationBarFlags = navigationBarFlags;
try {
mTarget.initializeInternal(params);
@@ -279,4 +277,13 @@ final class IInputMethodInvoker {
logRemoteException(e);
}
}
+
+ @AnyThread
+ void removeStylusHandwritingWindow() {
+ try {
+ mTarget.removeStylusHandwritingWindow();
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b63592c2fedb..23ea39a3b022 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -286,8 +286,7 @@ final class InputMethodBindingController {
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
final InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
mSupportsStylusHw = info.supportsStylusHandwriting();
- mService.initializeImeLocked(mCurMethod, mCurToken, info.getConfigChanges(),
- mSupportsStylusHw);
+ mService.initializeImeLocked(mCurMethod, mCurToken, info.getConfigChanges());
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
mService.performOnCreateInlineSuggestionsRequestLocked();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 23f437392d2b..7bd835c73749 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -246,6 +246,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private static final int MSG_RESET_HANDWRITING = 1090;
private static final int MSG_START_HANDWRITING = 1100;
private static final int MSG_FINISH_HANDWRITING = 1110;
+ private static final int MSG_REMOVE_HANDWRITING_WINDOW = 1120;
private static final int MSG_SET_INTERACTIVE = 3030;
@@ -2719,13 +2720,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token,
- @android.content.pm.ActivityInfo.Config int configChanges, boolean supportStylusHw) {
+ @android.content.pm.ActivityInfo.Config int configChanges) {
if (DEBUG) {
Slog.v(TAG, "Sending attach of token: " + token + " for display: "
+ mCurTokenDisplayId);
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
- configChanges, supportStylusHw, getInputMethodNavButtonFlagsLocked());
+ configChanges, getInputMethodNavButtonFlagsLocked());
}
@AnyThread
@@ -2734,6 +2735,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@AnyThread
+ void scheduleRemoveStylusHandwritingWindow() {
+ mHandler.obtainMessage(MSG_REMOVE_HANDWRITING_WINDOW).sendToTarget();
+ }
+
+ @AnyThread
void scheduleNotifyImeUidToAudioService(int uid) {
mHandler.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
mHandler.obtainMessage(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid, 0 /* unused */)
@@ -4363,40 +4369,50 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private void add(int deviceId) {
synchronized (ImfLock.class) {
- if (mStylusIds == null) {
- mStylusIds = new IntArray();
- } else if (mStylusIds.indexOf(deviceId) != -1) {
- return;
- }
- Slog.d(TAG, "New Stylus device " + deviceId + " added.");
- mStylusIds.add(deviceId);
- // a new Stylus is detected. If IME supports handwriting and we don't have
- // handwriting initialized, lets do it now.
- if (!mHwController.getCurrentRequestId().isPresent()
- && mMethodMap.get(getSelectedMethodIdLocked())
- .supportsStylusHandwriting()) {
- scheduleResetStylusHandwriting();
- }
+ addStylusDeviceIdLocked(deviceId);
}
}
private void remove(int deviceId) {
synchronized (ImfLock.class) {
- if (mStylusIds == null || mStylusIds.size() == 0) {
- return;
- }
- if (mStylusIds.indexOf(deviceId) != -1) {
- mStylusIds.remove(deviceId);
- Slog.d(TAG, "Stylus device " + deviceId + " removed.");
- }
- if (mStylusIds.size() == 0) {
- mHwController.reset();
- }
+ removeStylusDeviceIdLocked(deviceId);
}
}
}, mHandler);
}
+ private void addStylusDeviceIdLocked(int deviceId) {
+ if (mStylusIds == null) {
+ mStylusIds = new IntArray();
+ } else if (mStylusIds.indexOf(deviceId) != -1) {
+ return;
+ }
+ Slog.d(TAG, "New Stylus deviceId" + deviceId + " added.");
+ mStylusIds.add(deviceId);
+ // a new Stylus is detected. If IME supports handwriting, and we don't have
+ // handwriting initialized, lets do it now.
+ if (!mHwController.getCurrentRequestId().isPresent()
+ && mBindingController.supportsStylusHandwriting()) {
+ scheduleResetStylusHandwriting();
+ }
+ }
+
+ private void removeStylusDeviceIdLocked(int deviceId) {
+ if (mStylusIds == null || mStylusIds.size() == 0) {
+ return;
+ }
+ int index;
+ if ((index = mStylusIds.indexOf(deviceId)) != -1) {
+ mStylusIds.remove(index);
+ Slog.d(TAG, "Stylus deviceId: " + deviceId + " removed.");
+ }
+ if (mStylusIds.size() == 0) {
+ // no more supported stylus(es) in system.
+ mHwController.reset();
+ scheduleRemoveStylusHandwritingWindow();
+ }
+ }
+
private static boolean isStylusDevice(InputDevice inputDevice) {
return inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)
|| inputDevice.supportsSource(InputDevice.SOURCE_BLUETOOTH_STYLUS);
@@ -4422,17 +4438,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
final long ident = Binder.clearCallingIdentity();
try {
- if (!mBindingController.supportsStylusHandwriting()) {
- Slog.w(TAG, "Stylus HW unsupported by IME. Ignoring addVirtualStylusId()");
- return;
- }
-
if (DEBUG) Slog.v(TAG, "Adding virtual stylus id for session");
- if (mStylusIds == null) {
- mStylusIds = new IntArray();
- }
- mStylusIds.add(VIRTUAL_STYLUS_ID_FOR_TEST);
- scheduleResetStylusHandwriting();
+ addStylusDeviceIdLocked(VIRTUAL_STYLUS_ID_FOR_TEST);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4441,10 +4448,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
private void removeVirtualStylusIdForTestSessionLocked() {
- int index;
- if ((index = mStylusIds.indexOf(VIRTUAL_STYLUS_ID_FOR_TEST)) != -1) {
- mStylusIds.remove(index);
- }
+ removeStylusDeviceIdLocked(VIRTUAL_STYLUS_ID_FOR_TEST);
}
private static IntArray getStylusInputDeviceIds(InputManager im) {
@@ -4922,6 +4926,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
return true;
+ case MSG_REMOVE_HANDWRITING_WINDOW:
+ synchronized (ImfLock.class) {
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
+ curMethod.removeStylusHandwritingWindow();
+ }
+ }
+ return true;
}
return false;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
new file mode 100644
index 000000000000..e46bb74003a2
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * A class to log events and useful metrics within the Context Hub service.
+ *
+ * The class holds a queue of the last NUM_EVENTS_TO_STORE events for each
+ * event category: nanoapp load, nanoapp unload, message from a nanoapp,
+ * message to a nanoapp, and context hub restarts. The dump() function
+ * will be called during debug dumps, giving access to the event information
+ * and aggregate data since the instantiation of this class.
+ *
+ * @hide
+ */
+public class ContextHubEventLogger {
+ private static final String TAG = "ContextHubEventLogger";
+
+ ContextHubEventLogger() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a nanoapp load event
+ *
+ * @param contextHubId the ID of the context hub
+ * @param nanoAppId the ID of the nanoapp
+ * @param nanoAppVersion the version of the nanoapp
+ * @param nanoAppSize the size in bytes of the nanoapp
+ * @param success whether the load was successful
+ */
+ public void logNanoAppLoad(int contextHubId, long nanoAppId, int nanoAppVersion,
+ long nanoAppSize, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a nanoapp unload event
+ *
+ * @param contextHubId the ID of the context hub
+ * @param nanoAppId the ID of the nanoapp
+ * @param success whether the unload was successful
+ */
+ public void logNanoAppUnload(int contextHubId, long nanoAppId, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs the event where a nanoapp sends a message to a client
+ *
+ * @param contextHubId the ID of the context hub
+ * @param message the message that was sent
+ */
+ public void logMessageFromNanoApp(int contextHubId, NanoAppMessage message) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs the event where a client sends a message to a nanoapp
+ *
+ * @param contextHubId the ID of the context hub
+ * @param message the message that was sent
+ * @param success whether the message was sent successfully
+ */
+ public void logMessageToNanoApp(int contextHubId, NanoAppMessage message, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a context hub restart event
+ *
+ * @param contextHubId the ID of the context hub
+ */
+ public void logContextHubRestarts(int contextHubId) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Creates a string representation of the logged events
+ *
+ * @return the dumped events
+ */
+ public String dump() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
+ public String toString() {
+ return dump();
+ }
+}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 729c521a4ce0..477b8da61e0f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,7 +86,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -1328,17 +1327,16 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
NotificationChannelGroup group = r.groups.get(groupId).clone();
- ArrayList channels = new ArrayList();
+ group.setChannels(new ArrayList<>());
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (groupId.equals(nc.getGroup())) {
- channels.add(nc);
+ group.addChannel(nc);
}
}
}
- group.setChannels(channels);
return group;
}
}
@@ -1351,10 +1349,7 @@ public class PreferencesHelper implements RankingConfig {
if (r == null) {
return null;
}
- if (r.groups.get(groupId) != null) {
- return r.groups.get(groupId).clone();
- }
- return null;
+ return r.groups.get(groupId);
}
}
@@ -1362,48 +1357,44 @@ public class PreferencesHelper implements RankingConfig {
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Objects.requireNonNull(pkg);
- List<NotificationChannelGroup> groups = new ArrayList<>();
+ Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
}
- Map<String, ArrayList<NotificationChannel>> groupedChannels = new HashMap();
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- nc.getGroup(), new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(nc.getGroup(), channels);
+ NotificationChannelGroup ncg = groups.get(nc.getGroup());
+ if (ncg == null) {
+ ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
+ groups.put(nc.getGroup(), ncg);
+
+ }
+ ncg.addChannel(nc);
}
} else {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- null, new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(null, channels);
+ nonGrouped.addChannel(nc);
}
}
}
- for (NotificationChannelGroup group : r.groups.values()) {
- ArrayList<NotificationChannel> channels =
- groupedChannels.getOrDefault(group.getId(), new ArrayList<>());
- if (includeEmpty || !channels.isEmpty()) {
- NotificationChannelGroup clone = group.clone();
- clone.setChannels(channels);
- groups.add(clone);
- }
+ if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+ groups.put(null, nonGrouped);
}
-
- if (includeNonGrouped && groupedChannels.containsKey(null)) {
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
- nonGrouped.setChannels(groupedChannels.get(null));
- groups.add(nonGrouped);
+ if (includeEmpty) {
+ for (NotificationChannelGroup group : r.groups.values()) {
+ if (!groups.containsKey(group.getId())) {
+ groups.put(group.getId(), group);
+ }
+ }
}
- return new ParceledListSlice<>(groups);
+ return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index da76738fa025..bb918d594167 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -257,6 +257,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
String name = "";
String filename = null;
String opt;
+ String configuration = null;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "--user":
@@ -274,6 +275,9 @@ final class OverlayManagerShellCommand extends ShellCommand {
case "--file":
filename = getNextArgRequired();
break;
+ case "--config":
+ configuration = getNextArgRequired();
+ break;
default:
err.println("Error: Unknown option: " + opt);
return 1;
@@ -307,7 +311,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
final String resourceName = getNextArgRequired();
final String typeStr = getNextArgRequired();
final String strData = String.join(" ", peekRemainingArgs());
- addOverlayValue(overlayBuilder, resourceName, typeStr, strData);
+ addOverlayValue(overlayBuilder, resourceName, typeStr, strData, configuration);
}
mInterface.commit(new OverlayManagerTransaction.Builder()
@@ -363,8 +367,9 @@ final class OverlayManagerShellCommand extends ShellCommand {
err.println("Error: value missing at line " + parser.getLineNumber());
return 1;
}
+ String config = parser.getAttributeValue(null, "config");
addOverlayValue(overlayBuilder, targetPackage + ':' + target,
- overlayType, value);
+ overlayType, value, config);
}
}
}
@@ -379,7 +384,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
}
private void addOverlayValue(FabricatedOverlay.Builder overlayBuilder,
- String resourceName, String typeString, String valueString) {
+ String resourceName, String typeString, String valueString, String configuration) {
final int type;
typeString = typeString.toLowerCase(Locale.getDefault());
if (TYPE_MAP.containsKey(typeString)) {
@@ -392,7 +397,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
}
}
if (type == TypedValue.TYPE_STRING) {
- overlayBuilder.setResourceValue(resourceName, type, valueString);
+ overlayBuilder.setResourceValue(resourceName, type, valueString, configuration);
} else {
final int intData;
if (valueString.startsWith("0x")) {
@@ -400,7 +405,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
} else {
intData = Integer.parseUnsignedInt(valueString);
}
- overlayBuilder.setResourceValue(resourceName, type, intData);
+ overlayBuilder.setResourceValue(resourceName, type, intData, configuration);
}
}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 45192b7bcdbe..d3d1cc5d05cb 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -109,6 +109,13 @@ public final class NativeTombstoneManager {
private void handleTombstone(File path) {
final String filename = path.getName();
+
+ // Clean up temporary files if they made it this far (e.g. if system server crashes).
+ if (filename.endsWith(".tmp")) {
+ path.delete();
+ return;
+ }
+
if (!filename.startsWith("tombstone_")) {
return;
}
@@ -561,6 +568,10 @@ public final class NativeTombstoneManager {
@Override
public void onEvent(int event, @Nullable String path) {
mHandler.post(() -> {
+ // Ignore .tmp files.
+ if (path.endsWith(".tmp")) {
+ return;
+ }
handleTombstone(new File(TOMBSTONE_DIR, path));
});
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e423ea9170d7..672c13c2dbf9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -911,7 +911,7 @@ public class UserManagerService extends IUserManager.Stub {
private @NonNull List<UserInfo> getUsersInternal(boolean excludePartial, boolean excludeDying,
boolean excludePreCreated) {
synchronized (mUsersLock) {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ ArrayList<UserInfo> users = new ArrayList<>(mUsers.size());
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
@@ -1767,6 +1767,32 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public List<UserHandle> getVisibleUsers() {
+ if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException("Caller needs MANAGE_USERS or INTERACT_ACROSS_USERS "
+ + "permission to get list of visible users");
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // TODO(b/2399825580): refactor into UserDisplayAssigner
+ synchronized (mUsersLock) {
+ int usersSize = mUsers.size();
+ ArrayList<UserHandle> visibleUsers = new ArrayList<>(usersSize);
+ for (int i = 0; i < usersSize; i++) {
+ UserInfo ui = mUsers.valueAt(i).info;
+ if (!ui.partial && !ui.preCreated && !mRemovingUserIds.get(ui.id)
+ && isUserVisibleUnchecked(ui.id)) {
+ visibleUsers.add(UserHandle.of(ui.id));
+ }
+ }
+ return visibleUsers;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public @NonNull String getUserName() {
final int callingUid = Binder.getCallingUid();
if (!hasQueryOrCreateUsersPermission()
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index f7a6eef29dee..d3a64bb0b9ac 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -62,6 +62,8 @@ public class PackageCacher {
StringBuilder sb = new StringBuilder(packageFile.getName());
sb.append('-');
sb.append(flags);
+ sb.append('-');
+ sb.append(packageFile.getAbsolutePath().hashCode());
return sb.toString();
}
@@ -171,7 +173,12 @@ public class PackageCacher {
}
final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
- return fromCacheEntry(bytes);
+ ParsedPackage parsed = fromCacheEntry(bytes);
+ if (!packageFile.getAbsolutePath().equals(parsed.getPath())) {
+ // Don't use this cache if the path doesn't match
+ return null;
+ }
+ return parsed;
} catch (Throwable e) {
Slog.w(TAG, "Error reading package cache: ", e);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 937a7891a849..1a1de0341aa0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -215,7 +215,6 @@ import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-import com.android.server.wm.WindowManagerService;
import java.io.File;
import java.io.FileNotFoundException;
@@ -2082,22 +2081,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
- boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
- // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
- // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
- // need to call IKeyguardService#keyguardGoingAway here.
- return handleStartTransitionForKeyguardLw(keyguardGoingAway
- && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation,
- keyguardOccluding, duration);
+ return handleTransitionForKeyguardLw(false /* startKeyguardExitAnimation */,
+ false /* notifyOccluded */);
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
- handleStartTransitionForKeyguardLw(
- keyguardGoingAway, false /* keyguardOccludingStarted */,
- 0 /* duration */);
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
+ // When KEYGUARD_GOING_AWAY app transition is canceled, we need to trigger relevant
+ // IKeyguardService calls to sync keyguard status in WindowManagerService and SysUI.
+ handleTransitionForKeyguardLw(
+ keyguardGoingAwayCancelled /* startKeyguardExitAnimation */,
+ true /* notifyOccluded */);
}
});
@@ -3262,31 +3258,39 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
} else {
- setKeyguardOccludedLw(occluded, false /* force */,
- false /* transitionStarted */);
+ setKeyguardOccludedLw(occluded, true /* notify */);
}
}
@Override
- public int applyKeyguardOcclusionChange(boolean transitionStarted) {
+ public int applyKeyguardOcclusionChange(boolean notify) {
if (mKeyguardOccludedChanged) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
+ mPendingKeyguardOccluded);
- if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */,
- transitionStarted)) {
+ if (setKeyguardOccludedLw(mPendingKeyguardOccluded, notify)) {
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
}
}
return 0;
}
- private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway,
- boolean keyguardOccluding, long duration) {
- final int redoLayout = applyKeyguardOcclusionChange(keyguardOccluding);
+ /**
+ * Called when keyguard related app transition starts, or cancelled.
+ *
+ * @param startKeyguardExitAnimation Trigger IKeyguardService#startKeyguardExitAnimation to
+ * start keyguard exit animation.
+ * @param notifyOccluded Trigger IKeyguardService#setOccluded binder call to notify whether
+ * the top activity can occlude the keyguard or not.
+ *
+ * @return Whether the flags have changed and we have to redo the layout.
+ */
+ private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
+ boolean notifyOccluded) {
+ final int redoLayout = applyKeyguardOcclusionChange(notifyOccluded);
if (redoLayout != 0) return redoLayout;
- if (keyguardGoingAway) {
+ if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
- startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
+ startKeyguardExitAnimation(SystemClock.uptimeMillis());
}
return 0;
}
@@ -3518,28 +3522,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* Updates the occluded state of the Keyguard.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
- * @param force notify the occluded status to KeyguardService and update flags even though
- * occlude status doesn't change.
- * @param transitionStarted {@code true} if keyguard (un)occluded transition started.
+ * @param notify Notify keyguard occlude status change immediately via
+ * {@link com.android.internal.policy.IKeyguardService}.
* @return Whether the flags have changed and we have to redo the layout.
*/
- private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force,
- boolean transitionStarted) {
+ private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
- if (isKeyguardOccluded() == isOccluded && !force) {
+ if (isKeyguardOccluded() == isOccluded) {
return false;
}
-
- final boolean showing = mKeyguardDelegate.isShowing();
- final boolean animate = showing && !isOccluded;
- // When remote animation is enabled for keyguard (un)occlude transition, KeyguardService
- // uses remote animation start as a signal to update its occlusion status ,so we don't need
- // to notify here.
- final boolean notify = !WindowManagerService.sEnableRemoteKeyguardOccludeAnimation
- || !transitionStarted;
- mKeyguardDelegate.setOccluded(isOccluded, animate, notify);
- return showing;
+ mKeyguardDelegate.setOccluded(isOccluded, notify);
+ return mKeyguardDelegate.isShowing();
}
/** {@inheritDoc} */
@@ -4935,10 +4929,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime) {
if (mKeyguardDelegate != null) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
- mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
+ mKeyguardDelegate.startKeyguardExitAnimation(startTime);
}
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 9ad15c8b538d..f00edf3a76c5 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -49,7 +49,7 @@ public final class SingleKeyGestureDetector {
// Key code of current key down event, reset when key up.
private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
- private volatile boolean mHandledByLongPress = false;
+ private boolean mHandledByLongPress = false;
private final Handler mHandler;
private long mLastDownTime = 0;
@@ -194,8 +194,8 @@ public final class SingleKeyGestureDetector {
mHandledByLongPress = true;
mHandler.removeMessages(MSG_KEY_LONG_PRESS);
mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
- final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, mActiveRule.mKeyCode,
- 0, mActiveRule);
+ final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
+ mActiveRule);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -274,13 +274,26 @@ public final class SingleKeyGestureDetector {
}
private boolean interceptKeyUp(KeyEvent event) {
- mHandler.removeMessages(MSG_KEY_LONG_PRESS);
- mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
if (mActiveRule == null) {
return false;
}
+ if (!mHandledByLongPress) {
+ final long eventTime = event.getEventTime();
+ if (eventTime < mLastDownTime + mActiveRule.getLongPressTimeoutMs()) {
+ mHandler.removeMessages(MSG_KEY_LONG_PRESS);
+ } else {
+ mHandledByLongPress = mActiveRule.supportLongPress();
+ }
+
+ if (eventTime < mLastDownTime + mActiveRule.getVeryLongPressTimeoutMs()) {
+ mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
+ } else {
+ mHandledByLongPress = mActiveRule.supportVeryLongPress();
+ }
+ }
+
if (mHandledByLongPress) {
mHandledByLongPress = false;
mKeyPressCounter = 0;
@@ -376,7 +389,6 @@ public final class SingleKeyGestureDetector {
if (DEBUG) {
Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode));
}
- mHandledByLongPress = true;
rule.onLongPress(mLastDownTime);
break;
case MSG_KEY_VERY_LONG_PRESS:
@@ -384,7 +396,6 @@ public final class SingleKeyGestureDetector {
Log.i(TAG, "Detect very long press "
+ KeyEvent.keyCodeToString(keyCode));
}
- mHandledByLongPress = true;
rule.onVeryLongPress(mLastDownTime);
break;
case MSG_KEY_DELAYED_PRESS:
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 4f00992c713e..2b0405073323 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -171,10 +171,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
void onKeyguardOccludedChangedLw(boolean occluded);
/**
- * Applies a keyguard occlusion change if one happened.
- * @param transitionStarted Whether keyguard (un)occlude transition is starting or not.
+ * @param notify {@code true} if the status change should be immediately notified via
+ * {@link com.android.internal.policy.IKeyguardService}
*/
- int applyKeyguardOcclusionChange(boolean transitionStarted);
+ int applyKeyguardOcclusionChange(boolean notify);
/**
* Interface to the Window Manager state associated with a particular
@@ -1129,11 +1129,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Notifies the keyguard to start fading out.
+ * @param startTime the start time of the animation in uptime milliseconds
*
- * @param startTime the start time of the animation in uptime milliseconds
- * @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index b79ac6f68be2..7737421654ee 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -249,10 +249,10 @@ public class KeyguardServiceDelegate {
}
}
- public void setOccluded(boolean isOccluded, boolean animate, boolean notify) {
+ public void setOccluded(boolean isOccluded, boolean notify) {
if (mKeyguardService != null && notify) {
- if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
- mKeyguardService.setOccluded(isOccluded, animate);
+ if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
+ mKeyguardService.setOccluded(isOccluded, false /* animate */);
}
mKeyguardState.occluded = isOccluded;
}
@@ -394,9 +394,9 @@ public class KeyguardServiceDelegate {
}
}
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime) {
if (mKeyguardService != null) {
- mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
+ mKeyguardService.startKeyguardExitAnimation(startTime, 0);
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index dad9584c6722..5a2fb18673ac 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -571,8 +571,7 @@ public class Notifier {
/**
* Called when there has been user activity.
*/
- public void onUserActivity(int displayGroupId, @PowerManager.UserActivityEvent int event,
- int uid) {
+ public void onUserActivity(int displayGroupId, int event, int uid) {
if (DEBUG) {
Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
}
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 9fe53fbfaf25..fec61ac8f2cf 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -74,8 +74,6 @@ public class PowerGroup {
private long mLastPowerOnTime;
private long mLastUserActivityTime;
private long mLastUserActivityTimeNoChangeLights;
- @PowerManager.UserActivityEvent
- private int mLastUserActivityEvent;
/** Timestamp (milliseconds since boot) of the last time the power group was awoken.*/
private long mLastWakeTime;
/** Timestamp (milliseconds since boot) of the last time the power group was put to sleep. */
@@ -246,7 +244,7 @@ public class PowerGroup {
return true;
}
- boolean dozeLocked(long eventTime, int uid, @PowerManager.GoToSleepReason int reason) {
+ boolean dozeLocked(long eventTime, int uid, int reason) {
if (eventTime < getLastWakeTimeLocked() || !isInteractive(mWakefulness)) {
return false;
}
@@ -255,14 +253,9 @@ public class PowerGroup {
try {
reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
- long millisSinceLastUserActivity = eventTime - Math.max(
- mLastUserActivityTimeNoChangeLights, mLastUserActivityTime);
Slog.i(TAG, "Powering off display group due to "
- + PowerManager.sleepReasonToString(reason)
- + " (groupId= " + getGroupId() + ", uid= " + uid
- + ", millisSinceLastUserActivity=" + millisSinceLastUserActivity
- + ", lastUserActivityEvent=" + PowerManager.userActivityEventToString(
- mLastUserActivityEvent) + ")...");
+ + PowerManager.sleepReasonToString(reason) + " (groupId= " + getGroupId()
+ + ", uid= " + uid + ")...");
setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
setWakefulnessLocked(WAKEFULNESS_DOZING, eventTime, uid, reason, /* opUid= */ 0,
@@ -273,16 +266,14 @@ public class PowerGroup {
return true;
}
- boolean sleepLocked(long eventTime, int uid, @PowerManager.GoToSleepReason int reason) {
+ boolean sleepLocked(long eventTime, int uid, int reason) {
if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
return false;
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "sleepPowerGroup");
try {
- Slog.i(TAG,
- "Sleeping power group (groupId=" + getGroupId() + ", uid=" + uid + ", reason="
- + PowerManager.sleepReasonToString(reason) + ")...");
+ Slog.i(TAG, "Sleeping power group (groupId=" + getGroupId() + ", uid=" + uid + ")...");
setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
setWakefulnessLocked(WAKEFULNESS_ASLEEP, eventTime, uid, reason, /* opUid= */0,
/* opPackageName= */ null, /* details= */ null);
@@ -296,20 +287,16 @@ public class PowerGroup {
return mLastUserActivityTime;
}
- void setLastUserActivityTimeLocked(long lastUserActivityTime,
- @PowerManager.UserActivityEvent int event) {
+ void setLastUserActivityTimeLocked(long lastUserActivityTime) {
mLastUserActivityTime = lastUserActivityTime;
- mLastUserActivityEvent = event;
}
public long getLastUserActivityTimeNoChangeLightsLocked() {
return mLastUserActivityTimeNoChangeLights;
}
- public void setLastUserActivityTimeNoChangeLightsLocked(long time,
- @PowerManager.UserActivityEvent int event) {
+ public void setLastUserActivityTimeNoChangeLightsLocked(long time) {
mLastUserActivityTimeNoChangeLights = time;
- mLastUserActivityEvent = event;
}
public int getUserActivitySummaryLocked() {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ca3599ca7fa0..2a1748c441dc 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1216,7 +1216,6 @@ public final class PowerManagerService extends SystemService
return;
}
- Slog.i(TAG, "onFlip(): Face " + (isFaceDown ? "down." : "up."));
mIsFaceDown = isFaceDown;
if (isFaceDown) {
final long currentTime = mClock.uptimeMillis();
@@ -1938,13 +1937,12 @@ public final class PowerManagerService extends SystemService
// Called from native code.
@SuppressWarnings("unused")
- private void userActivityFromNative(long eventTime, @PowerManager.UserActivityEvent int event,
- int displayId, int flags) {
+ private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
}
- private void userActivityInternal(int displayId, long eventTime,
- @PowerManager.UserActivityEvent int event, int flags, int uid) {
+ private void userActivityInternal(int displayId, long eventTime, int event, int flags,
+ int uid) {
synchronized (mLock) {
if (displayId == Display.INVALID_DISPLAY) {
if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
@@ -1995,12 +1993,11 @@ public final class PowerManagerService extends SystemService
@GuardedBy("mLock")
private boolean userActivityNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
- @PowerManager.UserActivityEvent int event, int flags, int uid) {
+ int event, int flags, int uid) {
final int groupId = powerGroup.getGroupId();
if (DEBUG_SPEW) {
Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
- + ", eventTime=" + eventTime
- + ", event=" + PowerManager.userActivityEventToString(event)
+ + ", eventTime=" + eventTime + ", event=" + event
+ ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
}
@@ -2035,7 +2032,7 @@ public final class PowerManagerService extends SystemService
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
if (eventTime > powerGroup.getLastUserActivityTimeNoChangeLightsLocked()
&& eventTime > powerGroup.getLastUserActivityTimeLocked()) {
- powerGroup.setLastUserActivityTimeNoChangeLightsLocked(eventTime, event);
+ powerGroup.setLastUserActivityTimeNoChangeLightsLocked(eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -2045,7 +2042,7 @@ public final class PowerManagerService extends SystemService
}
} else {
if (eventTime > powerGroup.getLastUserActivityTimeLocked()) {
- powerGroup.setLastUserActivityTimeLocked(eventTime, event);
+ powerGroup.setLastUserActivityTimeLocked(eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -2072,8 +2069,7 @@ public final class PowerManagerService extends SystemService
@WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "wakePowerGroupLocked: eventTime=" + eventTime
- + ", groupId=" + powerGroup.getGroupId()
- + ", reason=" + PowerManager.wakeReasonToString(reason) + ", uid=" + uid);
+ + ", groupId=" + powerGroup.getGroupId() + ", uid=" + uid);
}
if (mForceSuspendActive || !mSystemReady) {
return;
@@ -2096,11 +2092,11 @@ public final class PowerManagerService extends SystemService
@GuardedBy("mLock")
private boolean dozePowerGroupLocked(final PowerGroup powerGroup, long eventTime,
- @GoToSleepReason int reason, int uid) {
+ int reason, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "dozePowerGroup: eventTime=" + eventTime
- + ", groupId=" + powerGroup.getGroupId()
- + ", reason=" + PowerManager.sleepReasonToString(reason) + ", uid=" + uid);
+ + ", groupId=" + powerGroup.getGroupId() + ", reason=" + reason
+ + ", uid=" + uid);
}
if (!mSystemReady || !mBootCompleted) {
@@ -2111,12 +2107,10 @@ public final class PowerManagerService extends SystemService
}
@GuardedBy("mLock")
- private boolean sleepPowerGroupLocked(final PowerGroup powerGroup, long eventTime,
- @GoToSleepReason int reason, int uid) {
+ private boolean sleepPowerGroupLocked(final PowerGroup powerGroup, long eventTime, int reason,
+ int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "sleepPowerGroup: eventTime=" + eventTime
- + ", groupId=" + powerGroup.getGroupId()
- + ", reason=" + PowerManager.sleepReasonToString(reason) + ", uid=" + uid);
+ Slog.d(TAG, "sleepPowerGroup: eventTime=" + eventTime + ", uid=" + uid);
}
if (!mBootCompleted || !mSystemReady) {
return false;
@@ -2178,11 +2172,7 @@ public final class PowerManagerService extends SystemService
case WAKEFULNESS_DOZING:
traceMethodName = "goToSleep";
Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
- + " (uid " + uid + ", screenOffTimeout=" + mScreenOffTimeoutSetting
- + ", activityTimeoutWM=" + mUserActivityTimeoutOverrideFromWindowManager
- + ", maxDimRatio=" + mMaximumScreenDimRatioConfig
- + ", maxDimDur=" + mMaximumScreenDimDurationConfig + ")...");
-
+ + " (uid " + uid + ")...");
mLastGlobalSleepTime = eventTime;
mLastGlobalSleepReason = reason;
mLastGlobalSleepTimeRealtime = mClock.elapsedRealtime();
@@ -4267,7 +4257,7 @@ public final class PowerManagerService extends SystemService
void onUserActivity() {
synchronized (mLock) {
mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP).setLastUserActivityTimeLocked(
- mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER);
+ mClock.uptimeMillis());
}
}
@@ -5655,8 +5645,7 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
- public void userActivity(int displayId, long eventTime,
- @PowerManager.UserActivityEvent int event, int flags) {
+ public void userActivity(int displayId, long eventTime, int event, int flags) {
final long now = mClock.uptimeMillis();
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index fe4aa534df6f..df902c2916ba 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -657,7 +657,7 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
// Now that we have finally received all the data, we can tell mStats about it.
synchronized (mStats) {
- mStats.addHistoryEventLocked(
+ mStats.recordHistoryEventLocked(
elapsedRealtime,
uptime,
BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 202beb377b2c..968f9161b3c1 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -108,6 +108,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.BatteryStatsHistory.HistoryStepDetailsCalculator;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
@@ -173,7 +174,6 @@ public class BatteryStatsImpl extends BatteryStats {
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
- private static final boolean DEBUG_HISTORY = false;
// TODO: remove "tcp" from network methods, since we measure total stats.
@@ -322,6 +322,11 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
+ @NonNull
+ BatteryStatsHistory copyHistory() {
+ return mHistory.copy();
+ }
+
@VisibleForTesting
public final class UidToRemove {
private final int mStartUid;
@@ -413,7 +418,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (changed) {
final long uptimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs);
}
}
}
@@ -668,16 +673,16 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Mapping isolated uids to the actual owning app uid.
*/
- final SparseIntArray mIsolatedUids = new SparseIntArray();
+ private final SparseIntArray mIsolatedUids = new SparseIntArray();
/**
* Internal reference count of isolated uids.
*/
- final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+ private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
/**
* The statistics we have collected organized by uids.
*/
- final SparseArray<BatteryStatsImpl.Uid> mUidStats = new SparseArray<>();
+ private final SparseArray<BatteryStatsImpl.Uid> mUidStats = new SparseArray<>();
// A set of pools of currently active timers. When a timer is queried, we will divide the
// elapsed time by the number of active timers to arrive at that timer's share of the time.
@@ -685,20 +690,21 @@ public class BatteryStatsImpl extends BatteryStats {
// changes.
@VisibleForTesting
protected ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
- final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
- final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
- final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
- final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
- final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
+ private final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
+ private final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
+ private final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
+ new SparseArray<>();
+ private final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
+ private final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
// Last partial timers we use for distributing CPU usage.
@VisibleForTesting
@@ -713,69 +719,24 @@ public class BatteryStatsImpl extends BatteryStats {
protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(true);
private boolean mSystemReady;
- boolean mShuttingDown;
-
- final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
-
- long mHistoryBaseTimeMs;
- protected boolean mHaveBatteryLevel = false;
- protected boolean mRecordingHistory = false;
- int mNumHistoryItems;
-
- private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
- private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
-
- final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
- private SparseArray<HistoryTag> mHistoryTags;
- final Parcel mHistoryBuffer = Parcel.obtain();
- final HistoryItem mHistoryLastWritten = new HistoryItem();
- final HistoryItem mHistoryLastLastWritten = new HistoryItem();
- final HistoryItem mHistoryAddTmp = new HistoryItem();
- int mNextHistoryTagIdx = 0;
- int mNumHistoryTagChars = 0;
- int mHistoryBufferLastPos = -1;
- int mActiveHistoryStates = 0xffffffff;
- int mActiveHistoryStates2 = 0xffffffff;
- long mLastHistoryElapsedRealtimeMs = 0;
- long mTrackRunningHistoryElapsedRealtimeMs = 0;
- long mTrackRunningHistoryUptimeMs = 0;
+ private boolean mShuttingDown;
+
+ private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
+ private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
+ new HistoryStepDetailsCalculatorImpl();
+
+ private boolean mHaveBatteryLevel = false;
+ private boolean mBatteryPluggedIn;
+ private int mBatteryStatus;
+ private int mBatteryLevel;
+ private int mBatteryPlugType;
+ private int mBatteryChargeUah;
+ private int mBatteryHealth;
+ private int mBatteryTemperature;
+ private int mBatteryVoltageMv = -1;
@NonNull
- final BatteryStatsHistory mBatteryStatsHistory;
-
- final HistoryItem mHistoryCur = new HistoryItem();
-
- // Used by computeHistoryStepDetails
- HistoryStepDetails mLastHistoryStepDetails = null;
- byte mLastHistoryStepLevel = 0;
- final HistoryStepDetails mCurHistoryStepDetails = new HistoryStepDetails();
- final HistoryStepDetails mTmpHistoryStepDetails = new HistoryStepDetails();
-
- /**
- * Total time (in milliseconds) spent executing in user code.
- */
- long mLastStepCpuUserTimeMs;
- long mCurStepCpuUserTimeMs;
- /**
- * Total time (in milliseconds) spent executing in kernel code.
- */
- long mLastStepCpuSystemTimeMs;
- long mCurStepCpuSystemTimeMs;
- /**
- * Times from /proc/stat (but measured in milliseconds).
- */
- long mLastStepStatUserTimeMs;
- long mLastStepStatSystemTimeMs;
- long mLastStepStatIOWaitTimeMs;
- long mLastStepStatIrqTimeMs;
- long mLastStepStatSoftIrqTimeMs;
- long mLastStepStatIdleTimeMs;
- long mCurStepStatUserTimeMs;
- long mCurStepStatSystemTimeMs;
- long mCurStepStatIOWaitTimeMs;
- long mCurStepStatIrqTimeMs;
- long mCurStepStatSoftIrqTimeMs;
- long mCurStepStatIdleTimeMs;
+ private final BatteryStatsHistory mHistory;
private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
@@ -1391,7 +1352,6 @@ public class BatteryStatsImpl extends BatteryStats {
int mDischargeUnplugLevel;
int mDischargePlugLevel;
int mDischargeCurrentLevel;
- int mCurrentBatteryLevel;
int mLowDischargeAmountSinceCharge;
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
@@ -1443,7 +1403,6 @@ public class BatteryStatsImpl extends BatteryStats {
private int mNumConnectivityChange;
- private int mBatteryVoltageMv = -1;
private int mEstimatedBatteryCapacityMah = -1;
private int mLastLearnedBatteryCapacityUah = -1;
@@ -1627,28 +1586,27 @@ public class BatteryStatsImpl extends BatteryStats {
}
public BatteryStatsImpl(Clock clock) {
- this(clock, (File) null);
+ this(clock, null);
}
public BatteryStatsImpl(Clock clock, File historyDirectory) {
init(clock);
+ mHandler = null;
+ mConstants = new Constants(mHandler);
mStartClockTimeMs = clock.currentTimeMillis();
mCheckinFile = null;
mDailyFile = null;
if (historyDirectory == null) {
mStatsFile = null;
- mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
+ mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
} else {
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
- mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer, historyDirectory,
- this::getMaxHistoryFiles);
+ mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
}
- mHandler = null;
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
mUserInfoProvider = null;
- mConstants = new Constants(mHandler);
- clearHistoryLocked();
}
private void init(Clock clock) {
@@ -3911,406 +3869,188 @@ public class BatteryStatsImpl extends BatteryStats {
return kmt;
}
- /**
- * Returns the index for the specified tag. If this is the first time the tag is encountered
- * while writing the current history buffer, the method returns
- * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
- */
- private int writeHistoryTag(HistoryTag tag) {
- if (tag.string == null) {
- Slog.wtfStack(TAG, "writeHistoryTag called with null name");
- }
-
- final int stringLength = tag.string.length();
- if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) {
- Slog.e(TAG, "Long battery history tag: " + tag.string);
- tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH);
- }
-
- Integer idxObj = mHistoryTagPool.get(tag);
- int idx;
- if (idxObj != null) {
- idx = idxObj;
- if ((idx & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- mHistoryTagPool.put(tag, idx & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
- }
- return idx;
- } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
- idx = mNextHistoryTagIdx;
- HistoryTag key = new HistoryTag();
- key.setTo(tag);
- tag.poolIdx = idx;
- mHistoryTagPool.put(key, idx);
- mNextHistoryTagIdx++;
-
- mNumHistoryTagChars += stringLength + 1;
- if (mHistoryTags != null) {
- mHistoryTags.put(idx, key);
- }
- return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
- } else {
- // Tag pool overflow: include the tag itself in the parcel
- return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
- }
- }
-
- /*
- The history delta format uses flags to denote further data in subsequent ints in the parcel.
-
- There is always the first token, which may contain the delta time, or an indicator of
- the length of the time (int or long) following this token.
-
- First token: always present,
- 31 23 15 7 0
- â–ˆM|L|K|J|I|H|G|Fâ–ˆE|D|C|B|A|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆ
-
- T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately
- follows containing the time, and 0x7ffff indicates a long immediately follows with the
- delta time.
- A: battery level changed and an int follows with battery data.
- B: state changed and an int follows with state change data.
- C: state2 has changed and an int follows with state2 change data.
- D: wakelock/wakereason has changed and an wakelock/wakereason struct follows.
- E: event data has changed and an event struct follows.
- F: battery charge in coulombs has changed and an int with the charge follows.
- G: state flag denoting that the mobile radio was active.
- H: state flag denoting that the wifi radio was active.
- I: state flag denoting that a wifi scan occurred.
- J: state flag denoting that a wifi full lock was held.
- K: state flag denoting that the gps was on.
- L: state flag denoting that a wakelock was held.
- M: state flag denoting that the cpu was running.
-
- Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows
- with the time delta.
-
- Battery level int: if A in the first token is set,
- 31 23 15 7 0
- â–ˆL|L|L|L|L|L|L|Tâ–ˆT|T|T|T|T|T|T|Tâ–ˆT|V|V|V|V|V|V|Vâ–ˆV|V|V|V|V|V|V|Dâ–ˆ
-
- D: indicates that extra history details follow.
- V: the battery voltage.
- T: the battery temperature.
- L: the battery level (out of 100).
-
- State change int: if B in the first token is set,
- 31 23 15 7 0
- â–ˆS|S|S|H|H|H|P|Pâ–ˆF|E|D|C|B| | |Aâ–ˆ | | | | | | | â–ˆ | | | | | | | â–ˆ
-
- A: wifi multicast was on.
- B: battery was plugged in.
- C: screen was on.
- D: phone was scanning for signal.
- E: audio was on.
- F: a sensor was active.
-
- State2 change int: if C in the first token is set,
- 31 23 15 7 0
- â–ˆM|L|K|J|I|H|H|Gâ–ˆF|E|D|C| | | | â–ˆ | | | | | | | â–ˆ |B|B|B|A|A|A|Aâ–ˆ
-
- A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}.
- B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4.
- C: a bluetooth scan was active.
- D: the camera was active.
- E: bluetooth was on.
- F: a phone call was active.
- G: the device was charging.
- H: 2 bits indicating the device-idle (doze) state: off, light, full
- I: the flashlight was on.
- J: wifi was on.
- K: wifi was running.
- L: video was playing.
- M: power save mode was on.
-
- Wakelock/wakereason struct: if D in the first token is set,
- TODO(adamlesinski): describe wakelock/wakereason struct.
-
- Event struct: if E in the first token is set,
- TODO(adamlesinski): describe the event struct.
-
- History step details struct: if D in the battery level int is set,
- TODO(adamlesinski): describe the history step details struct.
-
- Battery charge int: if F in the first token is set, an int representing the battery charge
- in coulombs follows.
- */
+ private class HistoryStepDetailsCalculatorImpl implements HistoryStepDetailsCalculator {
+ private final HistoryStepDetails mDetails = new HistoryStepDetails();
- @GuardedBy("this")
- public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
- if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
- dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
- cur.writeToParcel(dest, 0);
- return;
- }
+ private boolean mHasHistoryStepDetails;
- final long deltaTime = cur.time - last.time;
- final int lastBatteryLevelInt = buildBatteryLevelInt(last);
- final int lastStateInt = buildStateInt(last);
+ private int mLastHistoryStepLevel;
- int deltaTimeToken;
- if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
- deltaTimeToken = BatteryStatsHistory.DELTA_TIME_LONG;
- } else if (deltaTime >= BatteryStatsHistory.DELTA_TIME_ABS) {
- deltaTimeToken = BatteryStatsHistory.DELTA_TIME_INT;
- } else {
- deltaTimeToken = (int)deltaTime;
- }
- int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
- final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
- ? BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG : 0;
- final boolean computeStepDetails = includeStepDetails != 0
- || mLastHistoryStepDetails == null;
- final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
- final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
- if (batteryLevelIntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG;
- }
- final int stateInt = buildStateInt(cur);
- final boolean stateIntChanged = stateInt != lastStateInt;
- if (stateIntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
- }
- final boolean state2IntChanged = cur.states2 != last.states2;
- if (state2IntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
- }
- if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
- firstToken |= BatteryStatsHistory.DELTA_WAKELOCK_FLAG;
- }
- if (cur.eventCode != HistoryItem.EVENT_NONE) {
- firstToken |= BatteryStatsHistory.DELTA_EVENT_FLAG;
- }
-
- final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
- if (batteryChargeChanged) {
- firstToken |= BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG;
- }
- dest.writeInt(firstToken);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
- + " deltaTime=" + deltaTime);
-
- if (deltaTimeToken >= BatteryStatsHistory.DELTA_TIME_INT) {
- if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
- dest.writeInt((int)deltaTime);
- } else {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
- dest.writeLong(deltaTime);
- }
- }
- if (batteryLevelIntChanged) {
- dest.writeInt(batteryLevelInt);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
- + Integer.toHexString(batteryLevelInt)
- + " batteryLevel=" + cur.batteryLevel
- + " batteryTemp=" + cur.batteryTemperature
- + " batteryVolt=" + (int)cur.batteryVoltage);
- }
- if (stateIntChanged) {
- dest.writeInt(stateInt);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
- + Integer.toHexString(stateInt)
- + " batteryStatus=" + cur.batteryStatus
- + " batteryHealth=" + cur.batteryHealth
- + " batteryPlugType=" + cur.batteryPlugType
- + " states=0x" + Integer.toHexString(cur.states));
- }
- if (state2IntChanged) {
- dest.writeInt(cur.states2);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
- + Integer.toHexString(cur.states2));
- }
- if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
- int wakeLockIndex;
- int wakeReasonIndex;
- if (cur.wakelockTag != null) {
- wakeLockIndex = writeHistoryTag(cur.wakelockTag);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
- + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
- } else {
- wakeLockIndex = 0xffff;
- }
- if (cur.wakeReasonTag != null) {
- wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
- + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
- } else {
- wakeReasonIndex = 0xffff;
- }
- dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
- if (cur.wakelockTag != null
- && (wakeLockIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.wakelockTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
- }
- if (cur.wakeReasonTag != null
- && (wakeReasonIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.wakeReasonTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
- }
- }
- if (cur.eventCode != HistoryItem.EVENT_NONE) {
- final int index = writeHistoryTag(cur.eventTag);
- final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
- dest.writeInt(codeAndIndex);
- if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.eventTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
+ /**
+ * Total time (in milliseconds) spent executing in user code.
+ */
+ private long mLastStepCpuUserTimeMs;
+ private long mCurStepCpuUserTimeMs;
+ /**
+ * Total time (in milliseconds) spent executing in kernel code.
+ */
+ private long mLastStepCpuSystemTimeMs;
+ private long mCurStepCpuSystemTimeMs;
+ /**
+ * Times from /proc/stat (but measured in milliseconds).
+ */
+ private long mLastStepStatUserTimeMs;
+ private long mLastStepStatSystemTimeMs;
+ private long mLastStepStatIOWaitTimeMs;
+ private long mLastStepStatIrqTimeMs;
+ private long mLastStepStatSoftIrqTimeMs;
+ private long mLastStepStatIdleTimeMs;
+ private long mCurStepStatUserTimeMs;
+ private long mCurStepStatSystemTimeMs;
+ private long mCurStepStatIOWaitTimeMs;
+ private long mCurStepStatIrqTimeMs;
+ private long mCurStepStatSoftIrqTimeMs;
+ private long mCurStepStatIdleTimeMs;
+
+ @Override
+ public HistoryStepDetails getHistoryStepDetails() {
+ if (mBatteryLevel >= mLastHistoryStepLevel && mHasHistoryStepDetails) {
+ mLastHistoryStepLevel = mBatteryLevel;
+ return null;
}
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
- + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
- + cur.eventTag.string);
- }
- if (computeStepDetails) {
+
+ // Perform a CPU update right after we do this collection, so we have started
+ // collecting good data for the next step.
+ requestImmediateCpuUpdate();
+
if (mPlatformIdleStateCallback != null) {
- mCurHistoryStepDetails.statSubsystemPowerState =
+ mDetails.statSubsystemPowerState =
mPlatformIdleStateCallback.getSubsystemLowPowerStats();
if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
- mCurHistoryStepDetails.statSubsystemPowerState);
-
- }
- computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
- if (includeStepDetails != 0) {
- mCurHistoryStepDetails.writeToParcel(dest);
- }
- cur.stepDetails = mCurHistoryStepDetails;
- mLastHistoryStepDetails = mCurHistoryStepDetails;
- } else {
- cur.stepDetails = null;
- }
- if (mLastHistoryStepLevel < cur.batteryLevel) {
- mLastHistoryStepDetails = null;
- }
- mLastHistoryStepLevel = cur.batteryLevel;
-
- if (batteryChargeChanged) {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
- dest.writeInt(cur.batteryChargeUah);
- }
- dest.writeDouble(cur.modemRailChargeMah);
- dest.writeDouble(cur.wifiRailChargeMah);
- }
-
- private int buildBatteryLevelInt(HistoryItem h) {
- return ((((int)h.batteryLevel)<<25)&0xfe000000)
- | ((((int)h.batteryTemperature)<<15)&0x01ff8000)
- | ((((int)h.batteryVoltage)<<1)&0x00007ffe);
- }
-
- private int buildStateInt(HistoryItem h) {
- int plugType = 0;
- if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_AC) != 0) {
- plugType = 1;
- } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_USB) != 0) {
- plugType = 2;
- } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
- plugType = 3;
- }
- return ((h.batteryStatus & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK)
- << BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
- | ((h.batteryHealth & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK)
- << BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
- | ((plugType & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK)
- << BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
- | (h.states & (~BatteryStatsHistory.STATE_BATTERY_MASK));
- }
-
- private void computeHistoryStepDetails(final HistoryStepDetails out,
- final HistoryStepDetails last) {
- final HistoryStepDetails tmp = last != null ? mTmpHistoryStepDetails : out;
-
- // Perform a CPU update right after we do this collection, so we have started
- // collecting good data for the next step.
- requestImmediateCpuUpdate();
-
- if (last == null) {
- // We are not generating a delta, so all we need to do is reset the stats
- // we will later be doing a delta from.
- final int NU = mUidStats.size();
- for (int i=0; i<NU; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- }
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
- tmp.clear();
- return;
- }
- if (DEBUG) {
- Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
- + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
- + " irq=" + mLastStepStatIrqTimeMs + " sirq="
- + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
- Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
- + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
- + " irq=" + mCurStepStatIrqTimeMs + " sirq="
- + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
- }
- out.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
- out.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
- out.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
- out.statSystemTime = (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
- out.statIOWaitTime = (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
- out.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
- out.statSoftIrqTime = (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
- out.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
- out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
- out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
- out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
- final int NU = mUidStats.size();
- for (int i=0; i<NU; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- final int totalUTimeMs = (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
- final int totalSTimeMs = (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
- final int totalTimeMs = totalUTimeMs + totalSTimeMs;
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- if (totalTimeMs <= (out.appCpuUTime3 + out.appCpuSTime3)) {
- continue;
- }
- if (totalTimeMs <= (out.appCpuUTime2 + out.appCpuSTime2)) {
- out.appCpuUid3 = uid.mUid;
- out.appCpuUTime3 = totalUTimeMs;
- out.appCpuSTime3 = totalSTimeMs;
+ mDetails.statSubsystemPowerState);
+ }
+
+ if (!mHasHistoryStepDetails) {
+ // We are not generating a delta, so all we need to do is reset the stats
+ // we will later be doing a delta from.
+ final int uidCount = mUidStats.size();
+ for (int i = 0; i < uidCount; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+ uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+ }
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ mDetails.clear();
} else {
- out.appCpuUid3 = out.appCpuUid2;
- out.appCpuUTime3 = out.appCpuUTime2;
- out.appCpuSTime3 = out.appCpuSTime2;
- if (totalTimeMs <= (out.appCpuUTime1 + out.appCpuSTime1)) {
- out.appCpuUid2 = uid.mUid;
- out.appCpuUTime2 = totalUTimeMs;
- out.appCpuSTime2 = totalSTimeMs;
- } else {
- out.appCpuUid2 = out.appCpuUid1;
- out.appCpuUTime2 = out.appCpuUTime1;
- out.appCpuSTime2 = out.appCpuSTime1;
- out.appCpuUid1 = uid.mUid;
- out.appCpuUTime1 = totalUTimeMs;
- out.appCpuSTime1 = totalSTimeMs;
+ if (DEBUG) {
+ Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
+ + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
+ + " irq=" + mLastStepStatIrqTimeMs + " sirq="
+ + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
+ Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
+ + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
+ + " irq=" + mCurStepStatIrqTimeMs + " sirq="
+ + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
+ }
+ mDetails.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
+ mDetails.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
+ mDetails.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
+ mDetails.statSystemTime =
+ (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
+ mDetails.statIOWaitTime =
+ (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
+ mDetails.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
+ mDetails.statSoftIrqTime =
+ (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
+ mDetails.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
+ mDetails.appCpuUid1 = mDetails.appCpuUid2 = mDetails.appCpuUid3 = -1;
+ mDetails.appCpuUTime1 = mDetails.appCpuUTime2 = mDetails.appCpuUTime3 = 0;
+ mDetails.appCpuSTime1 = mDetails.appCpuSTime2 = mDetails.appCpuSTime3 = 0;
+ final int uidCount = mUidStats.size();
+ for (int i = 0; i < uidCount; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ final int totalUTimeMs =
+ (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
+ final int totalSTimeMs =
+ (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
+ final int totalTimeMs = totalUTimeMs + totalSTimeMs;
+ uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+ uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+ if (totalTimeMs <= (mDetails.appCpuUTime3 + mDetails.appCpuSTime3)) {
+ continue;
+ }
+ if (totalTimeMs <= (mDetails.appCpuUTime2 + mDetails.appCpuSTime2)) {
+ mDetails.appCpuUid3 = uid.mUid;
+ mDetails.appCpuUTime3 = totalUTimeMs;
+ mDetails.appCpuSTime3 = totalSTimeMs;
+ } else {
+ mDetails.appCpuUid3 = mDetails.appCpuUid2;
+ mDetails.appCpuUTime3 = mDetails.appCpuUTime2;
+ mDetails.appCpuSTime3 = mDetails.appCpuSTime2;
+ if (totalTimeMs <= (mDetails.appCpuUTime1 + mDetails.appCpuSTime1)) {
+ mDetails.appCpuUid2 = uid.mUid;
+ mDetails.appCpuUTime2 = totalUTimeMs;
+ mDetails.appCpuSTime2 = totalSTimeMs;
+ } else {
+ mDetails.appCpuUid2 = mDetails.appCpuUid1;
+ mDetails.appCpuUTime2 = mDetails.appCpuUTime1;
+ mDetails.appCpuSTime2 = mDetails.appCpuSTime1;
+ mDetails.appCpuUid1 = uid.mUid;
+ mDetails.appCpuUTime1 = totalUTimeMs;
+ mDetails.appCpuSTime1 = totalSTimeMs;
+ }
+ }
}
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ }
+
+ mHasHistoryStepDetails = mBatteryLevel <= mLastHistoryStepLevel;
+ mLastHistoryStepLevel = mBatteryLevel;
+
+ return mDetails;
+ }
+
+ public void addCpuStats(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
+ int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
+ int statSoftIrqTimeMs, int statIdleTimeMs) {
+ if (DEBUG) {
+ Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
+ + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
+ + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
+ + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
}
+ mCurStepCpuUserTimeMs += totalUTimeMs;
+ mCurStepCpuSystemTimeMs += totalSTimeMs;
+ mCurStepStatUserTimeMs += statUserTimeMs;
+ mCurStepStatSystemTimeMs += statSystemTimeMs;
+ mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
+ mCurStepStatIrqTimeMs += statIrqTimeMs;
+ mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
+ mCurStepStatIdleTimeMs += statIdleTimeMs;
+ }
+
+ @Override
+ public void clear() {
+ mHasHistoryStepDetails = false;
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
}
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
}
@GuardedBy("this")
@Override
public void commitCurrentHistoryBatchLocked() {
- mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+ mHistory.commitCurrentHistoryBatchLocked();
}
@GuardedBy("this")
@@ -4326,191 +4066,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- if (!mHaveBatteryLevel || !mRecordingHistory) {
- return;
- }
-
- final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
- final int diffStates = mHistoryLastWritten.states^(cur.states&mActiveHistoryStates);
- final int diffStates2 = mHistoryLastWritten.states2^(cur.states2&mActiveHistoryStates2);
- final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
- final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
- if (DEBUG) {
- Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
- + Integer.toHexString(diffStates) + " lastDiff="
- + Integer.toHexString(lastDiffStates) + " diff2="
- + Integer.toHexString(diffStates2) + " lastDiff2="
- + Integer.toHexString(lastDiffStates2));
- }
- if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
- && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
- && (diffStates2&lastDiffStates2) == 0
- && (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence)
- && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
- && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
- && mHistoryLastWritten.stepDetails == null
- && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
- || cur.eventCode == HistoryItem.EVENT_NONE)
- && mHistoryLastWritten.batteryLevel == cur.batteryLevel
- && mHistoryLastWritten.batteryStatus == cur.batteryStatus
- && mHistoryLastWritten.batteryHealth == cur.batteryHealth
- && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
- && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
- && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
- // We can merge this new change in with the last one. Merging is
- // allowed as long as only the states have changed, and within those states
- // as long as no bit has changed both between now and the last entry, as
- // well as the last entry and the one before it (so we capture any toggles).
- if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
- mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
- mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
- mHistoryBufferLastPos = -1;
- elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
- // If the last written history had a wakelock tag, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have a wakelock tag.
- if (mHistoryLastWritten.wakelockTag != null) {
- cur.wakelockTag = cur.localWakelockTag;
- cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
- }
- // If the last written history had a wake reason tag, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have a wakelock tag.
- if (mHistoryLastWritten.wakeReasonTag != null) {
- cur.wakeReasonTag = cur.localWakeReasonTag;
- cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
- }
- // If the last written history had an event, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have an event.
- if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
- cur.eventCode = mHistoryLastWritten.eventCode;
- cur.eventTag = cur.localEventTag;
- cur.eventTag.setTo(mHistoryLastWritten.eventTag);
- }
- mHistoryLastWritten.setTo(mHistoryLastLastWritten);
- }
- final int dataSize = mHistoryBuffer.dataSize();
-
- if (dataSize >= mConstants.MAX_HISTORY_BUFFER) {
- //open a new history file.
- final long start = SystemClock.uptimeMillis();
- writeHistoryLocked();
- if (DEBUG) {
- Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
- + (SystemClock.uptimeMillis() - start));
- }
- mBatteryStatsHistory.startNextFile();
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
- mHistoryBufferLastPos = -1;
- mHistoryLastWritten.clear();
- mHistoryLastLastWritten.clear();
-
- // Mark every entry in the pool with a flag indicating that the tag
- // has not yet been encountered while writing the current history buffer.
- for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
- entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
- }
- // Make a copy of mHistoryCur.
- HistoryItem copy = new HistoryItem();
- copy.setTo(cur);
- // startRecordingHistory will reset mHistoryCur.
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
- // Add the copy into history buffer.
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, copy);
- return;
- }
-
- if (dataSize == 0) {
- // The history is currently empty; we need it to start with a time stamp.
- cur.currentTime = mClock.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
- }
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
- }
-
- @GuardedBy("this")
- private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
- if (mBatteryStatsHistoryIterator != null) {
- throw new IllegalStateException("Can't do this while iterating history!");
- }
- mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
- mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
- mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
- mHistoryLastWritten.tagsFirstOccurrence = hasTags;
- mHistoryLastWritten.states &= mActiveHistoryStates;
- mHistoryLastWritten.states2 &= mActiveHistoryStates2;
- writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
- mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
- cur.wakelockTag = null;
- cur.wakeReasonTag = null;
- cur.eventCode = HistoryItem.EVENT_NONE;
- cur.eventTag = null;
- cur.tagsFirstOccurrence = false;
- if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
- + " now " + mHistoryBuffer.dataPosition()
- + " size is now " + mHistoryBuffer.dataSize());
- }
-
- @GuardedBy("this")
- void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
- if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
- final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
- final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
- if (diffUptimeMs < (diffElapsedMs - 20)) {
- final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
- mHistoryAddTmp.setTo(mHistoryLastWritten);
- mHistoryAddTmp.wakelockTag = null;
- mHistoryAddTmp.wakeReasonTag = null;
- mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
- mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
- addHistoryRecordInnerLocked(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
- }
- }
- mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
- mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
- mTrackRunningHistoryUptimeMs = uptimeMs;
- addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
- }
-
- @GuardedBy("this")
- void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
- }
-
- @GuardedBy("this")
- public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+ public void recordHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
String name, int uid) {
- mHistoryCur.eventCode = code;
- mHistoryCur.eventTag = mHistoryCur.localEventTag;
- mHistoryCur.eventTag.string = name;
- mHistoryCur.eventTag.uid = uid;
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
- }
-
- @GuardedBy("this")
- void clearHistoryLocked() {
- if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
- mHistoryBaseTimeMs = 0;
- mLastHistoryElapsedRealtimeMs = 0;
- mTrackRunningHistoryElapsedRealtimeMs = 0;
- mTrackRunningHistoryUptimeMs = 0;
-
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
- mHistoryLastLastWritten.clear();
- mHistoryLastWritten.clear();
- mHistoryTagPool.clear();
- mNextHistoryTagIdx = 0;
- mNumHistoryTagChars = 0;
- mHistoryBufferLastPos = -1;
- mActiveHistoryStates = 0xffffffff;
- mActiveHistoryStates2 = 0xffffffff;
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, code, name, uid);
}
@GuardedBy("this")
@@ -4663,13 +4221,13 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(code, name, uid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, code, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, code, name, uid);
}
@GuardedBy("this")
public void noteCurrentTimeChangedLocked(long currentTimeMs,
long elapsedRealtimeMs, long uptimeMs) {
- recordCurrentTimeChangeLocked(currentTimeMs, elapsedRealtimeMs, uptimeMs);
+ mHistory.recordCurrentTimeChange(elapsedRealtimeMs, uptimeMs, currentTimeMs);
}
@GuardedBy("this")
@@ -4686,7 +4244,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mRecordAllHistory) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
}
@GuardedBy("this")
@@ -4744,8 +4302,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mRecordAllHistory) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH,
- name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH, name, uid);
}
@GuardedBy("this")
@@ -4761,7 +4318,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_START, name, uid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
}
@GuardedBy("this")
@@ -4777,8 +4334,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_FINISH, name, uid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH,
- name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH, name, uid);
}
@GuardedBy("this")
@@ -4794,7 +4350,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_START, name, uid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
}
@GuardedBy("this")
@@ -4812,7 +4368,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
@GuardedBy("this")
@@ -4860,7 +4416,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < workSource.size(); ++i) {
uid = mapUid(workSource.getUid(i));
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
@@ -4869,7 +4425,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < workChains.size(); ++i) {
uid = mapUid(workChains.get(i).getAttributionUid());
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
@@ -4877,7 +4433,7 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
@@ -4952,7 +4508,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- addHistoryEventLocked(mSecRealtime, mSecUptime,
+ mHistory.recordEvent(mSecRealtime, mSecUptime,
HistoryItem.EVENT_PROC_FINISH, ent.getKey(), uids.keyAt(j));
}
}
@@ -4967,8 +4523,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- addHistoryEventLocked(mSecRealtime, mSecUptime,
- HistoryItem.EVENT_PROC_START, ent.getKey(), uids.keyAt(j));
+ mHistory.recordEvent(mSecRealtime, mSecUptime, HistoryItem.EVENT_PROC_START,
+ ent.getKey(), uids.keyAt(j));
}
}
}
@@ -5011,30 +4567,19 @@ public class BatteryStatsImpl extends BatteryStats {
if (mRecordAllHistory) {
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
mappedUid, 0)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_START, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
- + Integer.toHexString(mHistoryCur.states));
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName;
- mHistoryCur.wakelockTag.uid = mappedUid;
mWakeLockImportant = !unimportantForLogging;
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
- } else if (!mWakeLockImportant && !unimportantForLogging
- && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
- if (mHistoryLastWritten.wakelockTag != null) {
- // We'll try to update the last tag.
- mHistoryLastWritten.wakelockTag = null;
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName;
- mHistoryCur.wakelockTag.uid = mappedUid;
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
- }
- mWakeLockImportant = true;
+ mHistory.recordWakelockStartEvent(elapsedRealtimeMs, uptimeMs, historyName,
+ mappedUid);
+ } else if (!mWakeLockImportant && !unimportantForLogging) {
+ if (mHistory.maybeUpdateWakelockTag(elapsedRealtimeMs, uptimeMs, historyName,
+ mappedUid)) {
+ mWakeLockImportant = true;
+ }
}
mWakeLockNesting++;
}
@@ -5087,15 +4632,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
mappedUid, 0)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WAKE_LOCK_FLAG);
}
}
if (mappedUid >= 0) {
@@ -5286,7 +4829,7 @@ public class BatteryStatsImpl extends BatteryStats {
mappedUid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
historyName, mappedUid);
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
@@ -5339,7 +4882,7 @@ public class BatteryStatsImpl extends BatteryStats {
mappedUid, 0)) {
return;
}
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
historyName, mappedUid);
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
@@ -5361,15 +4904,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
- if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
- + Integer.toHexString(mHistoryCur.states));
aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
- mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
- mHistoryCur.wakeReasonTag.string = reason;
- mHistoryCur.wakeReasonTag.uid = 0;
+ mHistory.recordWakeupEvent(elapsedRealtimeMs, uptimeMs, reason);
mLastWakeupReason = reason;
mLastWakeupUptimeMs = uptimeMs;
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
@@ -5380,22 +4918,11 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
- int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
- int statSoftIrqTimeMs, int statIdleTimeMs) {
- if (DEBUG) {
- Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
- + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
- + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
- + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
- }
- mCurStepCpuUserTimeMs += totalUTimeMs;
- mCurStepCpuSystemTimeMs += totalSTimeMs;
- mCurStepStatUserTimeMs += statUserTimeMs;
- mCurStepStatSystemTimeMs += statSystemTimeMs;
- mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
- mCurStepStatIrqTimeMs += statIrqTimeMs;
- mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
- mCurStepStatIdleTimeMs += statIdleTimeMs;
+ int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
+ int statSoftIrqTimeMs, int statIdleTimeMs) {
+ mStepDetailsCalculator.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
+ statSystemTimeMs, statIOWaitTimeMs, statIrqTimeMs,
+ statSoftIrqTimeMs, statIdleTimeMs);
}
public void noteProcessDiedLocked(int uid, int pid) {
@@ -5425,10 +4952,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mSensorNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_SENSOR_ON_FLAG);
}
mSensorNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -5445,10 +4970,8 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mSensorNesting--;
if (mSensorNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_SENSOR_ON_FLAG);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopSensor(sensor, elapsedRealtimeMs);
@@ -5498,10 +5021,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
final int mappedUid = mapUid(uid);
if (mGpsNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_GPS_ON_FLAG);
}
mGpsNesting++;
@@ -5526,10 +5047,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int mappedUid = mapUid(uid);
mGpsNesting--;
if (mGpsNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_GPS_ON_FLAG);
stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
mGpsSignalQualityBin = -1;
}
@@ -5562,12 +5081,9 @@ public class BatteryStatsImpl extends BatteryStats {
if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtimeMs);
}
- mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
- | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs, signalLevel);
mGpsSignalQualityBin = signalLevel;
}
- return;
}
@GuardedBy("this")
@@ -5740,41 +5256,33 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- boolean updateHistory = false;
+ int startStates = 0;
+ int stopStates = 0;
if (Display.isDozeState(state) && !Display.isDozeState(oldState)) {
- mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ startStates |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs);
- updateHistory = true;
} else if (Display.isDozeState(oldState) && !Display.isDozeState(state)) {
- mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ stopStates |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
- updateHistory = true;
}
if (Display.isOnState(state)) {
- mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
- + Integer.toHexString(mHistoryCur.states));
+ startStates |= HistoryItem.STATE_SCREEN_ON_FLAG;
mScreenOnTimer.startRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.startRunningLocked(elapsedRealtimeMs);
}
- updateHistory = true;
} else if (Display.isOnState(oldState)) {
- mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
- + Integer.toHexString(mHistoryCur.states));
+ stopStates |= HistoryItem.STATE_SCREEN_ON_FLAG;
mScreenOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.stopRunningLocked(elapsedRealtimeMs);
}
- updateHistory = true;
}
- if (updateHistory) {
- if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
- + Display.stateToString(state));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ if (startStates != 0 || stopStates != 0) {
+ mHistory.recordStateChangeEvent(elapsedRealtimeMs, uptimeMs, startStates,
+ stopStates);
}
// Per screen state Cpu stats needed. Prepare to schedule an external sync.
@@ -5888,13 +5396,7 @@ public class BatteryStatsImpl extends BatteryStats {
long uptimeMs) {
if (mScreenBrightnessBin != overallBin) {
if (overallBin >= 0) {
- mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
- | (overallBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Screen brightness " + overallBin + " to: "
- + Integer.toHexString(mHistoryCur.states));
- }
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordScreenBrightnessEvent(elapsedRealtimeMs, uptimeMs, overallBin);
}
if (mScreenState == Display.STATE_ON) {
if (mScreenBrightnessBin >= 0) {
@@ -5911,8 +5413,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- public void noteUserActivityLocked(int uid, @PowerManager.UserActivityEvent int event,
- long elapsedRealtimeMs, long uptimeMs) {
+ public void noteUserActivityLocked(int uid, int event, long elapsedRealtimeMs, long uptimeMs) {
if (mOnBatteryInternal) {
uid = mapUid(uid);
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteUserActivityLocked(event);
@@ -5922,8 +5423,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid,
long elapsedRealtimeMs, long uptimeMs) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP,
- reason, reasonUid);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP, reason,
+ reasonUid);
}
@GuardedBy("this")
@@ -5942,7 +5443,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra,
long elapsedRealtimeMs, long uptimeMs) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
extra, type);
mNumConnectivityChange++;
}
@@ -5951,7 +5452,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
- addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ mHistory.recordEvent(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteMobileRadioApWakeupLocked();
}
@@ -5977,7 +5478,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
mMobileRadioActiveStartTimeMs = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
- mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
} else {
realElapsedRealtimeMs = timestampNs / (1000*1000);
long lastUpdateTimeMs = mMobileRadioActiveStartTimeMs;
@@ -5989,11 +5491,9 @@ public class BatteryStatsImpl extends BatteryStats {
mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtimeMs
- realElapsedRealtimeMs);
}
- mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
}
- if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mMobileRadioPowerState = powerState;
// Inform current RatBatteryStats that the modem active state might have changed.
@@ -6043,17 +5543,14 @@ public class BatteryStatsImpl extends BatteryStats {
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
- mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_POWER_SAVE_FLAG);
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_POWER_SAVE_FLAG);
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
@@ -6077,7 +5574,7 @@ public class BatteryStatsImpl extends BatteryStats {
nowLightIdling = true;
}
if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
activeReason, activeUid);
}
if (mDeviceIdling != nowIdling || mDeviceLightIdling != nowLightIdling) {
@@ -6107,11 +5604,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
if (mDeviceIdleMode != mode) {
- mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
- | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordDeviceIdleEvent(elapsedRealtimeMs, uptimeMs, mode);
long lastDuration = elapsedRealtimeMs - mLastIdleTimeStartMs;
mLastIdleTimeStartMs = elapsedRealtimeMs;
if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
@@ -6139,7 +5632,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void notePackageInstalledLocked(String pkgName, long versionCode,
long elapsedRealtimeMs, long uptimeMs) {
// XXX need to figure out what to do with long version codes.
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
pkgName, (int)versionCode);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
@@ -6151,8 +5644,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName,
long elapsedRealtimeMs, long uptimeMs) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
- HistoryItem.EVENT_PACKAGE_UNINSTALLED, pkgName, 0);
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
+ pkgName, 0);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
pc.mUpdate = true;
@@ -6181,10 +5674,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mPhoneOn) {
- mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_PHONE_IN_CALL_FLAG);
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
}
@@ -6193,10 +5684,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mPhoneOn) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_PHONE_IN_CALL_FLAG);
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
@@ -6234,11 +5723,12 @@ public class BatteryStatsImpl extends BatteryStats {
if (mUsbDataState != newState) {
mUsbDataState = newState;
if (connected) {
- mHistoryCur.states2 |= HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_USB_DATA_LINK_FLAG);
} else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_USB_DATA_LINK_FLAG);
}
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -6259,6 +5749,10 @@ public class BatteryStatsImpl extends BatteryStats {
long elapsedRealtimeMs, long uptimeMs) {
boolean scanning = false;
boolean newHistory = false;
+ int addStateFlag = 0;
+ int removeStateFlag = 0;
+ int newState = -1;
+ int newSignalStrength = -1;
mPhoneServiceStateRaw = state;
mPhoneSimStateRaw = simState;
@@ -6287,10 +5781,8 @@ public class BatteryStatsImpl extends BatteryStats {
scanning = true;
strengthBin = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalScanningTimer.isRunningLocked()) {
- mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
+ addStateFlag = HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
- + Integer.toHexString(mHistoryCur.states));
mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
simState, strengthBin);
@@ -6300,9 +5792,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!scanning) {
// If we are no longer scanning, then stop the scanning timer.
if (mPhoneSignalScanningTimer.isRunningLocked()) {
- mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
- + Integer.toHexString(mHistoryCur.states));
+ removeStateFlag = HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
@@ -6311,10 +5801,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mPhoneServiceState != state) {
- mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
- | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + state + " to: "
- + Integer.toHexString(mHistoryCur.states));
+ newState = state;
newHistory = true;
mPhoneServiceState = state;
}
@@ -6328,11 +5815,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
- mHistoryCur.states =
- (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
- | (strengthBin << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
- + Integer.toHexString(mHistoryCur.states));
+ newSignalStrength = strengthBin;
newHistory = true;
FrameworkStatsLog.write(
FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
@@ -6343,7 +5826,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (newHistory) {
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordPhoneStateChangeEvent(elapsedRealtimeMs, uptimeMs,
+ addStateFlag, removeStateFlag, newState, newSignalStrength);
}
}
@@ -6467,11 +5951,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
- mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
- | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordDataConnectionTypeChangeEvent(elapsedRealtimeMs, uptimeMs, bin);
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
elapsedRealtimeMs);
@@ -6544,10 +6024,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
- mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_WIFI_ON_FLAG);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-off", ExternalStatsSync.UPDATE_WIFI);
@@ -6557,10 +6035,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mWifiOn) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_WIFI_ON_FLAG);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-on", ExternalStatsSync.UPDATE_WIFI);
@@ -6571,10 +6047,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mAudioOnNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_AUDIO_ON_FLAG);
mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mAudioOnNesting++;
@@ -6589,10 +6063,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mAudioOnNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_AUDIO_ON_FLAG);
mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6603,10 +6075,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mVideoOnNesting == 0) {
- mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_VIDEO_ON_FLAG);
mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mVideoOnNesting++;
@@ -6621,10 +6091,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mVideoOnNesting == 0) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_VIDEO_ON_FLAG);
mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6635,10 +6103,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting > 0) {
mAudioOnNesting = 0;
- mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_AUDIO_ON_FLAG);
mAudioOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6651,10 +6117,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting > 0) {
mVideoOnNesting = 0;
- mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_VIDEO_ON_FLAG);
mVideoOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6706,10 +6170,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mFlashlightOnNesting++ == 0) {
- mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_FLASHLIGHT_FLAG);
mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6723,10 +6185,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mFlashlightOnNesting == 0) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_FLASHLIGHT_FLAG);
mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6737,10 +6197,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mCameraOnNesting++ == 0) {
- mHistoryCur.states2 |= HistoryItem.STATE2_CAMERA_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Camera on to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_CAMERA_FLAG);
mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6754,10 +6212,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mCameraOnNesting == 0) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_CAMERA_FLAG);
mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6768,10 +6224,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting > 0) {
mCameraOnNesting = 0;
- mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_CAMERA_FLAG);
mCameraOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6784,10 +6238,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting > 0) {
mFlashlightOnNesting = 0;
- mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_FLASHLIGHT_FLAG);
mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6804,10 +6256,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (mBluetoothScanNesting == 0) {
- mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
mBluetoothScanTimer.startRunningLocked(elapsedRealtimeMs);
}
mBluetoothScanNesting++;
@@ -6848,10 +6298,8 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mBluetoothScanNesting--;
if (mBluetoothScanNesting == 0) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6886,10 +6334,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mBluetoothScanNesting > 0) {
mBluetoothScanNesting = 0;
- mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6929,7 +6375,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
- addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ mHistory.recordEvent(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteWifiRadioApWakeupLocked();
}
@@ -6945,15 +6391,14 @@ public class BatteryStatsImpl extends BatteryStats {
if (uid > 0) {
noteWifiRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
}
- mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG);
mWifiActiveTimer.startRunningLocked(elapsedRealtimeMs);
} else {
- mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG);
mWifiActiveTimer.stopRunningLocked(timestampNs / (1000 * 1000));
}
- if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiRadioPowerState = powerState;
}
}
@@ -6961,10 +6406,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (!mGlobalWifiRunning) {
- mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_WIFI_RUNNING_FLAG);
mGlobalWifiRunning = true;
mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
int N = ws.size();
@@ -7032,10 +6475,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_WIFI_RUNNING_FLAG);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
int N = ws.size();
@@ -7083,12 +6524,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
mWifiSupplState = supplState;
mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtimeMs);
- mHistoryCur.states2 =
- (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
- | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Wifi suppl state " + supplState + " to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordWifiSupplicantStateChangeEvent(elapsedRealtimeMs, uptimeMs, supplState);
}
}
@@ -7117,12 +6553,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
- mHistoryCur.states2 =
- (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
- | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
- if (DEBUG_HISTORY) Slog.v(TAG, "Wifi signal strength " + strengthBin + " to: "
- + Integer.toHexString(mHistoryCur.states2));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordWifiSignalStrengthChangeEvent(elapsedRealtimeMs, uptimeMs,
+ strengthBin);
} else {
stopAllWifiSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
}
@@ -7135,10 +6567,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiFullLockNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_FULL_LOCK_FLAG);
}
mWifiFullLockNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -7149,10 +6579,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_FULL_LOCK_FLAG);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFullWifiLockReleasedLocked(elapsedRealtimeMs);
@@ -7168,10 +6596,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiScanNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_SCAN_FLAG);
}
mWifiScanNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -7187,10 +6613,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_SCAN_FLAG);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiScanStoppedLocked(elapsedRealtimeMs);
@@ -7215,14 +6639,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mWifiMulticastNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
-
+ mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG);
// Start Wifi Multicast overall timer
if (!mWifiMulticastWakelockTimer.isRunningLocked()) {
- if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started");
mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@@ -7236,14 +6656,12 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG);
// Stop Wifi Multicast overall timer
if (mWifiMulticastWakelockTimer.isRunningLocked()) {
- if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped");
+ if (DEBUG) Slog.v(TAG, "Multicast Overall Timer Stopped");
mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@@ -7995,8 +7413,9 @@ public class BatteryStatsImpl extends BatteryStats {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
- recordCurrentTimeChangeLocked(currentTimeMs, mClock.elapsedRealtime(),
- mClock.uptimeMillis());
+ mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ currentTimeMs
+ );
return currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
}
return mStartClockTimeMs;
@@ -9417,14 +8836,14 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
- public void noteUserActivityLocked(@PowerManager.UserActivityEvent int event) {
+ public void noteUserActivityLocked(int type) {
if (mUserActivityCounters == null) {
initUserActivityLocked();
}
- if (event >= 0 && event < NUM_USER_ACTIVITY_TYPES) {
- mUserActivityCounters[event].stepAtomic();
+ if (type >= 0 && type < NUM_USER_ACTIVITY_TYPES) {
+ mUserActivityCounters[type].stepAtomic();
} else {
- Slog.w(TAG, "Unknown user activity event " + event + " was specified.",
+ Slog.w(TAG, "Unknown user activity type " + type + " was specified.",
new Throwable());
}
}
@@ -11228,18 +10647,19 @@ public class BatteryStatsImpl extends BatteryStats {
UserInfoProvider userInfoProvider) {
init(clock);
+ mHandler = new MyHandler(handler.getLooper());
+ mConstants = new Constants(mHandler);
+
if (systemDir == null) {
mStatsFile = null;
- mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
+ mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
- mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer, systemDir,
- this::getMaxHistoryFiles);
+ mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
- mHandler = new MyHandler(handler.getLooper());
- mConstants = new Constants(mHandler);
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -11248,7 +10668,6 @@ public class BatteryStatsImpl extends BatteryStats {
initTimes(uptimeUs, realtimeUs);
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
initDischarge(realtimeUs);
- clearHistoryLocked();
updateDailyDeadlineLocked();
mPlatformIdleStateCallback = cb;
mMeasuredEnergyRetriever = energyStatsCb;
@@ -11259,12 +10678,6 @@ public class BatteryStatsImpl extends BatteryStats {
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
}
- private int getMaxHistoryFiles() {
- synchronized (this) {
- return mConstants.MAX_HISTORY_FILES;
- }
- }
-
@VisibleForTesting
protected void initTimersAndCounters() {
mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
@@ -11346,7 +10759,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeUnplugLevel = 0;
mDischargePlugLevel = -1;
mDischargeCurrentLevel = 0;
- mCurrentBatteryLevel = 0;
+ mBatteryLevel = 0;
}
public void setPowerProfileLocked(PowerProfile profile) {
@@ -11733,7 +11146,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public int getHistoryUsedSize() {
- return mBatteryStatsHistory.getHistoryUsedSize();
+ return mHistory.getHistoryUsedSize();
}
@Override
@@ -11747,43 +11160,27 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@VisibleForTesting
public BatteryStatsHistoryIterator createBatteryStatsHistoryIterator() {
- return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
+ return mHistory.iterate();
}
@Override
public int getHistoryStringPoolSize() {
- return mHistoryTagPool.size();
+ return mHistory.getHistoryStringPoolSize();
}
@Override
public int getHistoryStringPoolBytes() {
- return mNumHistoryTagChars;
+ return mHistory.getHistoryStringPoolBytes();
}
@Override
public String getHistoryTagPoolString(int index) {
- ensureHistoryTagArray();
- HistoryTag historyTag = mHistoryTags.get(index);
- return historyTag != null ? historyTag.string : null;
+ return mHistory.getHistoryTagPoolString(index);
}
@Override
public int getHistoryTagPoolUid(int index) {
- ensureHistoryTagArray();
- HistoryTag historyTag = mHistoryTags.get(index);
- return historyTag != null ? historyTag.uid : Process.INVALID_UID;
- }
-
- private void ensureHistoryTagArray() {
- if (mHistoryTags != null) {
- return;
- }
-
- mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
- for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
- mHistoryTags.put(entry.getValue() & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG,
- entry.getKey());
- }
+ return mHistory.getHistoryTagPoolUid(index);
}
@Override
@@ -11793,15 +11190,11 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public void finishIteratingHistoryLocked() {
+ mBatteryStatsHistoryIterator.close();
mBatteryStatsHistoryIterator = null;
}
@Override
- public long getHistoryBaseTime() {
- return mHistoryBaseTimeMs;
- }
-
- @Override
public int getStartCount() {
return mStartCount;
}
@@ -11854,24 +11247,23 @@ public class BatteryStatsImpl extends BatteryStats {
long realtimeUs = mSecRealtime * 1000;
resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_ADB_COMMAND);
pullPendingStateUpdatesLocked();
- addHistoryRecordLocked(mSecRealtime, mSecUptime);
- mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
- = mCurrentBatteryLevel = mHistoryCur.batteryLevel;
+ mHistory.writeHistoryItem(mSecRealtime, mSecUptime);
+ mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel = mBatteryLevel;
mOnBatteryTimeBase.reset(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs);
- if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
+ if (!mBatteryPluggedIn) {
if (Display.isOnState(mScreenState)) {
- mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenOnUnplugLevel = mBatteryLevel;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = 0;
} else if (Display.isDozeState(mScreenState)) {
mDischargeScreenOnUnplugLevel = 0;
- mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenDozeUnplugLevel = mBatteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
- mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenOffUnplugLevel = mBatteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
@@ -12015,27 +11407,12 @@ public class BatteryStatsImpl extends BatteryStats {
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
- mLastHistoryStepDetails = null;
- mLastStepCpuUserTimeMs = mLastStepCpuSystemTimeMs = 0;
- mCurStepCpuUserTimeMs = mCurStepCpuSystemTimeMs = 0;
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
-
mNumAllUidCpuTimeReads = 0;
mNumUidsRemoved = 0;
initDischarge(elapsedRealtimeUs);
- clearHistoryLocked();
- if (mBatteryStatsHistory != null) {
- mBatteryStatsHistory.resetAllFiles();
- }
+ mHistory.reset();
// Flush external data, gathering snapshots, but don't process it since it is pre-reset data
mIgnoreNextExternalStats = true;
@@ -12058,7 +11435,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
uids.keyAt(j));
}
}
@@ -12483,9 +11860,8 @@ public class BatteryStatsImpl extends BatteryStats {
(long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
- mHistoryCur.wifiRailChargeMah +=
- (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordWifiConsumedCharge(elapsedRealtimeMs, uptimeMs,
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
mTmpRailStats.resetWifiTotalEnergyUsed();
if (uidEstimatedConsumptionMah != null) {
@@ -12598,9 +11974,8 @@ public class BatteryStatsImpl extends BatteryStats {
(long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
- mHistoryCur.modemRailChargeMah +=
- (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordWifiConsumedCharge(elapsedRealtimeMs, uptimeMs,
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
mTmpRailStats.resetCellularTotalEnergyUsed();
}
@@ -12868,8 +12243,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
- mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG);
}
}
@@ -14302,11 +13677,7 @@ public class BatteryStatsImpl extends BatteryStats {
mHandler.removeCallbacks(mDeferSetCharging);
if (mCharging != charging) {
mCharging = charging;
- if (charging) {
- mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
- } else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
- }
+ mHistory.setChargingState(charging);
mHandler.sendEmptyMessage(MSG_REPORT_CHARGING);
return true;
}
@@ -14320,6 +13691,15 @@ public class BatteryStatsImpl extends BatteryStats {
mSystemReady = true;
}
+ /**
+ * Force recording of all history events regardless of the "charging" state.
+ */
+ @VisibleForTesting
+ public void forceRecordAllHistory() {
+ mHistory.forceRecordAllHistory();
+ mRecordAllHistory = true;
+ }
+
@GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
@@ -14403,15 +13783,12 @@ public class BatteryStatsImpl extends BatteryStats {
mInitStepMode = mCurStepMode;
mModStepMode = 0;
pullPendingStateUpdatesLocked();
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
- + Integer.toHexString(mHistoryCur.states));
if (reset) {
- mRecordingHistory = true;
- startRecordingHistory(mSecRealtime, mSecUptime, reset);
+ mHistory.startRecordingHistory(mSecRealtime, mSecUptime, reset);
+ initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
- addHistoryRecordLocked(mSecRealtime, mSecUptime);
+ mBatteryPluggedIn = false;
+ mHistory.recordBatteryState(mSecRealtime, mSecUptime, level, mBatteryPluggedIn);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (Display.isOnState(screenState)) {
mDischargeScreenOnUnplugLevel = level;
@@ -14433,11 +13810,8 @@ public class BatteryStatsImpl extends BatteryStats {
} else {
mOnBattery = mOnBatteryInternal = false;
pullPendingStateUpdatesLocked();
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(mSecRealtime, mSecUptime);
+ mBatteryPluggedIn = true;
+ mHistory.recordBatteryState(mSecRealtime, mSecUptime, level, mBatteryPluggedIn);
mDischargeCurrentLevel = mDischargePlugLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
@@ -14452,45 +13826,12 @@ public class BatteryStatsImpl extends BatteryStats {
mModStepMode = 0;
}
if (doWrite || (mLastWriteTimeMs + (60 * 1000)) < mSecRealtime) {
- if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
+ if (mStatsFile != null && !mHistory.isReadOnly()) {
writeAsyncLocked();
}
}
}
- @GuardedBy("this")
- private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
- boolean reset) {
- mRecordingHistory = true;
- mHistoryCur.currentTime = mClock.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs,
- reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
- mHistoryCur);
- mHistoryCur.currentTime = 0;
- if (reset) {
- initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
- }
- }
-
- @GuardedBy("this")
- private void recordCurrentTimeChangeLocked(final long currentTimeMs,
- final long elapsedRealtimeMs, final long uptimeMs) {
- if (mRecordingHistory) {
- mHistoryCur.currentTime = currentTimeMs;
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
- mHistoryCur.currentTime = 0;
- }
- }
-
- @GuardedBy("this")
- private void recordShutdownLocked(final long currentTimeMs, final long elapsedRealtimeMs) {
- if (mRecordingHistory) {
- mHistoryCur.currentTime = currentTimeMs;
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
- mHistoryCur.currentTime = 0;
- }
- }
-
private void scheduleSyncExternalStatsLocked(String reason, int updateFlags) {
if (mExternalSync != null) {
mExternalSync.scheduleSync(reason, updateFlags);
@@ -14508,8 +13849,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
- reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
- status, plugType, level);
+ reportChangesToStatsLog(status, plugType, level);
final boolean onBattery = isOnBattery(plugType, status);
if (!mHaveBatteryLevel) {
@@ -14519,52 +13859,47 @@ public class BatteryStatsImpl extends BatteryStats {
// plugged in, then twiddle our state to correctly reflect that
// since we won't be going through the full setOnBattery().
if (onBattery == mOnBattery) {
- if (onBattery) {
- mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- } else {
- mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- }
+ mHistory.setPluggedInState(!onBattery);
}
+ mBatteryStatus = status;
+ mBatteryLevel = level;
+ mBatteryChargeUah = chargeUah;
+
// Always start out assuming charging, that will be updated later.
- mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.batteryChargeUah = chargeUah;
+ mHistory.setBatteryState(true /* charging */, status, level, chargeUah);
+
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
- } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
+ } else if (mBatteryLevel != level || mOnBattery != onBattery) {
recordDailyStatsIfNeededLocked(level >= 100 && onBattery, currentTimeMs);
}
- int oldStatus = mHistoryCur.batteryStatus;
+ int oldStatus = mBatteryStatus;
if (onBattery) {
mDischargeCurrentLevel = level;
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
+ if (!mHistory.isRecordingHistory()) {
+ mHistory.startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
} else if (level < 96 &&
status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
- if (!mRecordingHistory) {
- mRecordingHistory = true;
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
+ if (!mHistory.isRecordingHistory()) {
+ mHistory.startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
}
- mBatteryVoltageMv = voltageMv;
- mCurrentBatteryLevel = level;
if (mDischargePlugLevel < 0) {
mDischargePlugLevel = level;
}
if (onBattery != mOnBattery) {
- mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.batteryStatus = (byte)status;
- mHistoryCur.batteryHealth = (byte)health;
- mHistoryCur.batteryPlugType = (byte)plugType;
- mHistoryCur.batteryTemperature = (short)temp;
- mHistoryCur.batteryVoltage = (char) voltageMv;
- if (chargeUah < mHistoryCur.batteryChargeUah) {
+ mBatteryLevel = level;
+ mBatteryStatus = status;
+ mBatteryHealth = health;
+ mBatteryPlugType = plugType;
+ mBatteryTemperature = temp;
+ mBatteryVoltageMv = voltageMv;
+ mHistory.setBatteryState(status, level, health, plugType, temp, voltageMv, chargeUah);
+ if (chargeUah < mBatteryChargeUah) {
// Only record discharges
- final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
+ final long chargeDiff = (long) mBatteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -14576,12 +13911,12 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mHistoryCur.batteryChargeUah = chargeUah;
+ mBatteryChargeUah = chargeUah;
setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah);
} else {
boolean changed = false;
- if (mHistoryCur.batteryLevel != level) {
- mHistoryCur.batteryLevel = (byte)level;
+ if (mBatteryLevel != level) {
+ mBatteryLevel = level;
changed = true;
// TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
@@ -14589,33 +13924,33 @@ public class BatteryStatsImpl extends BatteryStats {
mExternalSync.scheduleSyncDueToBatteryLevelChange(
mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
- if (mHistoryCur.batteryStatus != status) {
- mHistoryCur.batteryStatus = (byte)status;
+ if (mBatteryStatus != status) {
+ mBatteryStatus = status;
changed = true;
}
- if (mHistoryCur.batteryHealth != health) {
- mHistoryCur.batteryHealth = (byte)health;
+ if (mBatteryHealth != health) {
+ mBatteryHealth = health;
changed = true;
}
- if (mHistoryCur.batteryPlugType != plugType) {
- mHistoryCur.batteryPlugType = (byte)plugType;
+ if (mBatteryPlugType != plugType) {
+ mBatteryPlugType = plugType;
changed = true;
}
- if (temp >= (mHistoryCur.batteryTemperature+10)
- || temp <= (mHistoryCur.batteryTemperature-10)) {
- mHistoryCur.batteryTemperature = (short)temp;
+ if (temp >= (mBatteryTemperature + 10)
+ || temp <= (mBatteryTemperature - 10)) {
+ mBatteryTemperature = temp;
changed = true;
}
- if (voltageMv > (mHistoryCur.batteryVoltage + 20)
- || voltageMv < (mHistoryCur.batteryVoltage - 20)) {
- mHistoryCur.batteryVoltage = (char) voltageMv;
+ if (voltageMv > (mBatteryVoltageMv + 20)
+ || voltageMv < (mBatteryVoltageMv - 20)) {
+ mBatteryVoltageMv = voltageMv;
changed = true;
}
- if (chargeUah >= (mHistoryCur.batteryChargeUah + 10)
- || chargeUah <= (mHistoryCur.batteryChargeUah - 10)) {
- if (chargeUah < mHistoryCur.batteryChargeUah) {
+ if (chargeUah >= (mBatteryChargeUah + 10)
+ || chargeUah <= (mBatteryChargeUah - 10)) {
+ if (chargeUah < mBatteryChargeUah) {
// Only record discharges
- final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
+ final long chargeDiff = (long) mBatteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -14627,9 +13962,10 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mHistoryCur.batteryChargeUah = chargeUah;
+ mBatteryChargeUah = chargeUah;
changed = true;
}
+
long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
| (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
| (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
@@ -14687,7 +14023,10 @@ public class BatteryStatsImpl extends BatteryStats {
mLastChargeStepLevel = level;
}
if (changed) {
- addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ mHistory.setBatteryState(mBatteryStatus, mBatteryLevel, mBatteryHealth,
+ mBatteryPlugType, mBatteryTemperature, mBatteryVoltageMv,
+ mBatteryChargeUah);
+ mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs);
}
}
if (!onBattery &&
@@ -14696,7 +14035,7 @@ public class BatteryStatsImpl extends BatteryStats {
// We don't record history while we are plugged in and fully charged
// (or when battery is not present). The next time we are
// unplugged, history will be cleared.
- mRecordingHistory = DEBUG;
+ mHistory.setHistoryRecordingEnabled(DEBUG);
}
mLastLearnedBatteryCapacityUah = chargeFullUah;
@@ -14715,17 +14054,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
// Inform StatsLog of setBatteryState changes.
- // If this is the first reporting, pass in recentPast == null.
- private void reportChangesToStatsLog(HistoryItem recentPast,
- final int status, final int plugType, final int level) {
+ private void reportChangesToStatsLog(final int status, final int plugType, final int level) {
+ if (!mHaveBatteryLevel) {
+ return;
+ }
- if (recentPast == null || recentPast.batteryStatus != status) {
+ if (mBatteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
- if (recentPast == null || recentPast.batteryPlugType != plugType) {
+ if (mBatteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
- if (recentPast == null || recentPast.batteryLevel != level) {
+ if (mBatteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
@@ -14795,7 +14135,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * mCurrentBatteryLevel) * 1000;
+ return (msPerLevel * mBatteryLevel) * 1000;
}
@Override
@@ -14825,7 +14165,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
+ return (msPerLevel * (100 - mBatteryLevel)) * 1000;
}
/*@hide */
@@ -15256,7 +14596,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void shutdownLocked() {
- recordShutdownLocked(mClock.currentTimeMillis(), mClock.elapsedRealtime());
+ mHistory.recordShutdownEvent(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ mClock.currentTimeMillis());
writeSyncLocked();
mShuttingDown = true;
}
@@ -15464,7 +14805,6 @@ public class BatteryStatsImpl extends BatteryStats {
PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
-
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
@@ -15475,9 +14815,20 @@ public class BatteryStatsImpl extends BatteryStats {
: DEFAULT_MAX_HISTORY_BUFFER_KB)
* 1024;
updateBatteryChargedDelayMsLocked();
+
+ onChange();
}
}
+ /**
+ * Propagates changes in constant values.
+ */
+ @VisibleForTesting
+ public void onChange() {
+ mHistory.setMaxHistoryFiles(MAX_HISTORY_FILES);
+ mHistory.setMaxHistoryBufferSize(MAX_HISTORY_BUFFER);
+ }
+
private void updateBatteryChargedDelayMsLocked() {
// a negative value indicates that we should ignore this override
final int delay = Settings.Global.getInt(mResolver,
@@ -15698,27 +15049,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void writeHistoryLocked() {
- if (mBatteryStatsHistory.getActiveFile() == null) {
- Slog.w(TAG, "writeHistoryLocked: no history file associated with this instance");
- return;
- }
-
if (mShuttingDown) {
return;
}
- Parcel p = Parcel.obtain();
- try {
- final long start = SystemClock.uptimeMillis();
- writeHistoryBuffer(p, true);
- if (DEBUG) {
- Slog.d(TAG, "writeHistoryBuffer duration ms:"
- + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
- }
- writeParcelToFileLocked(p, mBatteryStatsHistory.getActiveFile());
- } finally {
- p.recycle();
- }
+ mHistory.writeHistory();
}
private final ReentrantLock mWriteLock = new ReentrantLock();
@@ -15757,13 +15092,6 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- final AtomicFile activeHistoryFile = mBatteryStatsHistory.getActiveFile();
- if (activeHistoryFile == null) {
- Slog.w(TAG,
- "readLocked: no history file associated with this instance");
- return;
- }
-
mUidStats.clear();
Parcel stats = Parcel.obtain();
@@ -15776,7 +15104,7 @@ public class BatteryStatsImpl extends BatteryStats {
readSummaryFromParcel(stats);
if (DEBUG) {
Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ + " bytes:" + raw.length + " took ms:" + (SystemClock.uptimeMillis()
- start));
}
}
@@ -15788,126 +15116,19 @@ public class BatteryStatsImpl extends BatteryStats {
stats.recycle();
}
- Parcel history = Parcel.obtain();
- try {
- final long start = SystemClock.uptimeMillis();
- if (activeHistoryFile.exists()) {
- byte[] raw = activeHistoryFile.readFully();
- if (raw.length > 0) {
- history.unmarshall(raw, 0, raw.length);
- history.setDataPosition(0);
- readHistoryBuffer(history);
- }
- if (DEBUG) {
- Slog.d(TAG, "readLocked history file::"
- + activeHistoryFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- - start));
- }
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error reading battery history", e);
- clearHistoryLocked();
- mBatteryStatsHistory.resetAllFiles();
- } finally {
- history.recycle();
+ if (!mHistory.readSummary()) {
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
+ RESET_REASON_CORRUPT_FILE);
}
mEndPlatformVersion = Build.ID;
- if (mHistoryBuffer.dataPosition() > 0
- || mBatteryStatsHistory.getFilesNumbers().size() > 1) {
- mRecordingHistory = true;
- final long elapsedRealtimeMs = mClock.elapsedRealtime();
- final long uptimeMs = mClock.uptimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur);
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
- }
+ mHistory.continueRecordingHistory();
recordDailyStatsIfNeededLocked(false, mClock.currentTimeMillis());
}
@GuardedBy("this")
- void readHistoryBuffer(Parcel in) throws ParcelFormatException {
- final int version = in.readInt();
- if (version != BatteryStatsHistory.VERSION) {
- Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
- + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats");
- return;
- }
-
- final long historyBaseTime = in.readLong();
-
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
-
- int bufSize = in.readInt();
- int curPos = in.dataPosition();
- if (bufSize >= (mConstants.MAX_HISTORY_BUFFER*100)) {
- throw new ParcelFormatException("File corrupt: history data buffer too large " +
- bufSize);
- } else if ((bufSize&~3) != bufSize) {
- throw new ParcelFormatException("File corrupt: history data buffer not aligned " +
- bufSize);
- } else {
- if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
- + " bytes at " + curPos);
- mHistoryBuffer.appendFrom(in, curPos, bufSize);
- in.setDataPosition(curPos + bufSize);
- }
-
- if (DEBUG_HISTORY) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** OLD mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- mHistoryBaseTimeMs = historyBaseTime;
- if (DEBUG_HISTORY) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** NEW mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
-
- // We are just arbitrarily going to insert 1 minute from the sample of
- // the last run until samples in this run.
- if (mHistoryBaseTimeMs > 0) {
- long oldnow = mClock.elapsedRealtime();
- mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
- if (DEBUG_HISTORY) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- }
- }
-
- void writeHistoryBuffer(Parcel out, boolean inclData) {
- if (DEBUG_HISTORY) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** WRITING mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- sb.append(" mLastHistoryElapsedRealtimeMs: ");
- TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- out.writeInt(BatteryStatsHistory.VERSION);
- out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
- if (!inclData) {
- out.writeInt(0);
- out.writeInt(0);
- return;
- }
-
- out.writeInt(mHistoryBuffer.dataSize());
- if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
- + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
- out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- }
-
- @GuardedBy("this")
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
@@ -15917,31 +15138,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- boolean inclHistory = in.readBoolean();
- if (inclHistory) {
- readHistoryBuffer(in);
- mBatteryStatsHistory.readFromParcel(in);
- }
-
- mHistoryTagPool.clear();
- mNextHistoryTagIdx = 0;
- mNumHistoryTagChars = 0;
-
- int numTags = in.readInt();
- for (int i=0; i<numTags; i++) {
- int idx = in.readInt();
- String str = in.readString();
- int uid = in.readInt();
- HistoryTag tag = new HistoryTag();
- tag.string = str;
- tag.uid = uid;
- tag.poolIdx = idx;
- mHistoryTagPool.put(tag, idx);
- if (idx >= mNextHistoryTagIdx) {
- mNextHistoryTagIdx = idx+1;
- }
- mNumHistoryTagChars += tag.string.length() + 1;
- }
+ mHistory.readSummaryFromParcel(in);
mStartCount = in.readInt();
mUptimeUs = in.readLong();
@@ -15954,7 +15151,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
- mCurrentBatteryLevel = in.readInt();
+ mBatteryLevel = in.readInt();
mEstimatedBatteryCapacityMah = in.readInt();
mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
@@ -16457,19 +15654,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(VERSION);
- out.writeBoolean(inclHistory);
- if (inclHistory) {
- writeHistoryBuffer(out, true);
- mBatteryStatsHistory.writeToParcel(out);
- }
-
- out.writeInt(mHistoryTagPool.size());
- for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
- HistoryTag tag = ent.getKey();
- out.writeInt(ent.getValue());
- out.writeString(tag.string);
- out.writeInt(tag.uid);
- }
+ mHistory.writeSummaryToParcel(out, inclHistory);
out.writeInt(mStartCount);
out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
@@ -16482,7 +15667,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
- out.writeInt(mCurrentBatteryLevel);
+ out.writeInt(mBatteryLevel);
out.writeInt(mEstimatedBatteryCapacityMah);
out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 0cdd4d101459..c36d950b6cf6 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -22,7 +22,6 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Parcel;
import android.os.Process;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
@@ -32,10 +31,8 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.PowerProfile;
-import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -220,18 +217,7 @@ public class BatteryUsageStatsProvider {
}
BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
-
- // Make a copy of battery history to avoid concurrent modification.
- Parcel historyBuffer = Parcel.obtain();
- historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
- batteryStatsImpl.mHistoryBuffer.dataSize());
-
- final File systemDir =
- batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
- final BatteryStatsHistory batteryStatsHistory =
- new BatteryStatsHistory(historyBuffer, systemDir, null);
-
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
+ batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.copyHistory());
}
BatteryUsageStats stats = batteryUsageStatsBuilder.build();
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 06253a08d937..8b30995404f0 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -220,18 +220,17 @@ public class PowerStatsDataStorage {
public void write(byte[] data) {
if (data != null && data.length > 0) {
mLock.lock();
-
- long currentTimeMillis = System.currentTimeMillis();
try {
+ long currentTimeMillis = System.currentTimeMillis();
DataElement dataElement = new DataElement(data);
mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
currentTimeMillis);
mFileRotator.maybeRotate(currentTimeMillis);
} catch (IOException e) {
Slog.e(TAG, "Failed to write to on-device storage: " + e);
+ } finally {
+ mLock.unlock();
}
-
- mLock.unlock();
}
}
@@ -240,21 +239,31 @@ public class PowerStatsDataStorage {
* DataElement retrieved from on-device storage, callback is called.
*/
public void read(DataElementReadCallback callback) throws IOException {
- mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
+ mLock.lock();
+ try {
+ mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
+ } finally {
+ mLock.unlock();
+ }
}
/**
* Deletes all stored log data.
*/
public void deleteLogs() {
- File[] files = mDataStorageDir.listFiles();
- for (int i = 0; i < files.length; i++) {
- int versionDot = mDataStorageFilename.lastIndexOf('.');
- String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
- // Check that the stems before the version match.
- if (files[i].getName().startsWith(beforeVersionDot)) {
- files[i].delete();
+ mLock.lock();
+ try {
+ File[] files = mDataStorageDir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ int versionDot = mDataStorageFilename.lastIndexOf('.');
+ String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
+ // Check that the stems before the version match.
+ if (files[i].getName().startsWith(beforeVersionDot)) {
+ files[i].delete();
+ }
}
+ } finally {
+ mLock.unlock();
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index ca675973b2fd..39ead13b03fe 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -159,12 +159,12 @@ public final class PowerStatsLogger extends Handler {
EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
} catch (IOException e) {
- Slog.e(TAG, "Failed to write energy meter data to incident report.");
+ Slog.e(TAG, "Failed to write energy meter data to incident report.", e);
}
}
});
} catch (IOException e) {
- Slog.e(TAG, "Failed to write energy meter info to incident report.");
+ Slog.e(TAG, "Failed to write energy meter info to incident report.", e);
}
pos.flush();
@@ -200,12 +200,12 @@ public final class PowerStatsLogger extends Handler {
EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos, true);
if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
} catch (IOException e) {
- Slog.e(TAG, "Failed to write energy model data to incident report.");
+ Slog.e(TAG, "Failed to write energy model data to incident report.", e);
}
}
});
} catch (IOException e) {
- Slog.e(TAG, "Failed to write energy model info to incident report.");
+ Slog.e(TAG, "Failed to write energy model info to incident report.", e);
}
pos.flush();
@@ -241,12 +241,12 @@ public final class PowerStatsLogger extends Handler {
StateResidencyResultUtils.packProtoMessage(stateResidencyResult, pos);
if (DEBUG) StateResidencyResultUtils.print(stateResidencyResult);
} catch (IOException e) {
- Slog.e(TAG, "Failed to write residency data to incident report.");
+ Slog.e(TAG, "Failed to write residency data to incident report.", e);
}
}
});
} catch (IOException e) {
- Slog.e(TAG, "Failed to write residency data to incident report.");
+ Slog.e(TAG, "Failed to write residency data to incident report.", e);
}
pos.flush();
@@ -267,7 +267,7 @@ public final class PowerStatsLogger extends Handler {
final FileInputStream fis = new FileInputStream(cachedFile.getPath());
fis.read(dataCached);
} catch (IOException e) {
- Slog.e(TAG, "Failed to read cached data from file");
+ Slog.e(TAG, "Failed to read cached data from file", e);
}
// If the cached and current data are different, delete the data store.
@@ -291,7 +291,7 @@ public final class PowerStatsLogger extends Handler {
fos.write(data);
atomicCachedFile.finishWrite(fos);
} catch (IOException e) {
- Slog.e(TAG, "Failed to write current data to cached file");
+ Slog.e(TAG, "Failed to write current data to cached file", e);
}
}
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index cd2b8943ce11..8431f1cc0b4c 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -583,10 +584,17 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
return;
}
final SpellCheckerInfo sci = spellCheckerMap.get(sciId);
+ final int uid = Binder.getCallingUid();
+ if (!canCallerAccessSpellChecker(sci, uid, userId)) {
+ if (DBG) {
+ Slog.d(TAG, "Spell checker " + sci.getId()
+ + " is not visible to the caller " + uid);
+ }
+ return;
+ }
HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups =
tsd.mSpellCheckerBindGroups;
SpellCheckerBindGroup bindGroup = spellCheckerBindGroups.get(sciId);
- final int uid = Binder.getCallingUid();
if (bindGroup == null) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -649,20 +657,28 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
public SpellCheckerInfo[] getEnabledSpellCheckers(@UserIdInt int userId) {
verifyUser(userId);
+ final ArrayList<SpellCheckerInfo> spellCheckerList;
synchronized (mLock) {
final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
if (tsd == null) return null;
- ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList;
+ spellCheckerList = new ArrayList<>(tsd.mSpellCheckerList);
+ }
+ int size = spellCheckerList.size();
+ final int callingUid = Binder.getCallingUid();
+ for (int i = size - 1; i >= 0; i--) {
+ if (canCallerAccessSpellChecker(spellCheckerList.get(i), callingUid, userId)) {
+ continue;
+ }
if (DBG) {
- Slog.d(TAG, "getEnabledSpellCheckers: " + spellCheckerList.size());
- for (int i = 0; i < spellCheckerList.size(); ++i) {
- Slog.d(TAG,
- "EnabledSpellCheckers: " + spellCheckerList.get(i).getPackageName());
- }
+ Slog.d(TAG, "Spell checker " + spellCheckerList.get(i).getPackageName()
+ + " is not visible to the caller " + callingUid);
}
- return spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]);
+ spellCheckerList.remove(i);
}
+
+ return spellCheckerList.isEmpty() ? null
+ : spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]);
}
@Override
@@ -701,6 +717,26 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
}
}
+ /**
+ * Filter the access to spell checkers by rules of the package visibility. Return {@code true}
+ * if the given spell checker is the currently selected one or visible to the caller.
+ *
+ * @param sci The spell checker to check.
+ * @param callingUid The caller that is going to access the spell checker.
+ * @param userId The user id where the spell checker resides.
+ * @return {@code true} if caller is able to access the spell checker.
+ */
+ private boolean canCallerAccessSpellChecker(@NonNull SpellCheckerInfo sci, int callingUid,
+ @UserIdInt int userId) {
+ final SpellCheckerInfo currentSci = getCurrentSpellCheckerForUser(userId);
+ if (currentSci != null && currentSci.getId().equals(sci.getId())) {
+ return true;
+ }
+ final PackageManagerInternal pmInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ return !pmInternal.filterAppAccess(sci.getPackageName(), callingUid, userId);
+ }
+
private void setCurrentSpellCheckerLocked(@Nullable SpellCheckerInfo sci, TextServicesData tsd) {
final String sciId = (sci != null) ? sci.getId() : "";
if (DBG) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 6162d716b85e..edd1ef36deda 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -39,6 +39,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -141,6 +142,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
mPriorityCongfig.parse();
+ // Call SystemProperties.set() in mock app will throw exception because of permission.
+ if (!isForTesting) {
+ final boolean lazyHal = SystemProperties.getBoolean("ro.tuner.lazyhal", false);
+ if (!lazyHal) {
+ // The HAL is not a lazy HAL, enable the tuner server.
+ SystemProperties.set("tuner.server.enable", "true");
+ }
+ }
+
if (mMediaResourceManager == null) {
IBinder mediaResourceManagerBinder = getBinderService("media.resource_manager");
if (mediaResourceManagerBinder == null) {
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index eebd046b2601..be9053055fe3 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -31,9 +31,9 @@ abstract class AbstractVibratorStep extends Step {
public final VibratorController controller;
public final VibrationEffect.Composed effect;
public final int segmentIndex;
- public final long previousStepVibratorOffTimeout;
long mVibratorOnResult;
+ long mPendingVibratorOffDeadline;
boolean mVibratorCompleteCallbackReceived;
/**
@@ -43,19 +43,19 @@ abstract class AbstractVibratorStep extends Step {
* @param controller The vibrator that is playing the effect.
* @param effect The effect being played in this step.
* @param index The index of the next segment to be played by this step
- * @param previousStepVibratorOffTimeout The time the vibrator is expected to complete any
+ * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
* previous vibration and turn off. This is used to allow this step to
* be triggered when the completion callback is received, and can
* be used to play effects back-to-back.
*/
AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
super(conductor, startTime);
this.controller = controller;
this.effect = effect;
this.segmentIndex = index;
- this.previousStepVibratorOffTimeout = previousStepVibratorOffTimeout;
+ mPendingVibratorOffDeadline = pendingVibratorOffDeadline;
}
public int getVibratorId() {
@@ -69,27 +69,57 @@ abstract class AbstractVibratorStep extends Step {
@Override
public boolean acceptVibratorCompleteCallback(int vibratorId) {
- boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
- mVibratorCompleteCallbackReceived |= isSameVibrator;
+ if (getVibratorId() != vibratorId) {
+ return false;
+ }
+
// Only activate this step if a timeout was set to wait for the vibration to complete,
// otherwise we are waiting for the correct time to play the next step.
- return isSameVibrator && (previousStepVibratorOffTimeout > SystemClock.uptimeMillis());
+ boolean shouldAcceptCallback = mPendingVibratorOffDeadline > SystemClock.uptimeMillis();
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Received completion callback from " + vibratorId
+ + ", accepted = " + shouldAcceptCallback);
+ }
+
+ // The callback indicates this vibrator has stopped, reset the timeout.
+ mPendingVibratorOffDeadline = 0;
+ mVibratorCompleteCallbackReceived = true;
+ return shouldAcceptCallback;
}
@Override
public List<Step> cancel() {
return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
- /* cancelled= */ true, controller, previousStepVibratorOffTimeout));
+ /* cancelled= */ true, controller, mPendingVibratorOffDeadline));
}
@Override
public void cancelImmediately() {
- if (previousStepVibratorOffTimeout > SystemClock.uptimeMillis()) {
+ if (mPendingVibratorOffDeadline > SystemClock.uptimeMillis()) {
// Vibrator might be running from previous steps, so turn it off while canceling.
stopVibrating();
}
}
+ protected long handleVibratorOnResult(long vibratorOnResult) {
+ mVibratorOnResult = vibratorOnResult;
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Turned on vibrator " + getVibratorId() + ", result = " + mVibratorOnResult);
+ }
+ if (mVibratorOnResult > 0) {
+ // Vibrator was turned on by this step, with vibratorOnResult as the duration.
+ // Set an extra timeout to wait for the vibrator completion callback.
+ mPendingVibratorOffDeadline = SystemClock.uptimeMillis() + mVibratorOnResult
+ + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+ } else {
+ // Vibrator does not support the request or failed to turn on, reset callback deadline.
+ mPendingVibratorOffDeadline = 0;
+ }
+ return mVibratorOnResult;
+ }
+
protected void stopVibrating() {
if (VibrationThread.DEBUG) {
Slog.d(VibrationThread.TAG,
@@ -97,6 +127,7 @@ abstract class AbstractVibratorStep extends Step {
}
controller.off();
getVibration().stats().reportVibratorOff();
+ mPendingVibratorOffDeadline = 0;
}
protected void changeAmplitude(float amplitude) {
@@ -109,40 +140,29 @@ abstract class AbstractVibratorStep extends Step {
}
/**
- * Return the {@link VibrationStepConductor#nextVibrateStep} with same timings, only jumping
- * the segments.
- */
- protected List<Step> skipToNextSteps(int segmentsSkipped) {
- return nextSteps(startTime, previousStepVibratorOffTimeout, segmentsSkipped);
- }
-
- /**
- * Return the {@link VibrationStepConductor#nextVibrateStep} with same start and off timings
- * calculated from {@link #getVibratorOnDuration()}, jumping all played segments.
- *
- * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
- * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
+ * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
+ * calculated from {@link #getVibratorOnDuration()} based on the current
+ * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
*/
protected List<Step> nextSteps(int segmentsPlayed) {
- if (mVibratorOnResult <= 0) {
- // Vibration was not started, so just skip the played segments and keep timings.
- return skipToNextSteps(segmentsPlayed);
+ // Schedule next steps to run right away.
+ long nextStartTime = SystemClock.uptimeMillis();
+ if (mVibratorOnResult > 0) {
+ // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
+ // Schedule next steps for right after the vibration finishes.
+ nextStartTime += mVibratorOnResult;
}
- long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
- long nextVibratorOffTimeout =
- nextStartTime + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
- return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
+ return nextSteps(nextStartTime, segmentsPlayed);
}
/**
- * Return the {@link VibrationStepConductor#nextVibrateStep} with given start and off timings,
- * which might be calculated independently, jumping all played segments.
+ * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
+ * which might be calculated independently, and jumping all played segments from the effect.
*
- * <p>This should be used when the vibrator on/off state is not responsible for the steps
- * execution timings, e.g. while playing the vibrator amplitudes.
+ * <p>This should be used when the vibrator on/off state is not responsible for the step
+ * execution timing, e.g. while playing the vibrator amplitudes.
*/
- protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
- int segmentsPlayed) {
+ protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
int nextSegmentIndex = segmentIndex + segmentsPlayed;
int effectSize = effect.getSegments().size();
int repeatIndex = effect.getRepeatIndex();
@@ -154,7 +174,7 @@ abstract class AbstractVibratorStep extends Step {
nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
}
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
- nextSegmentIndex, vibratorOffTimeout);
+ nextSegmentIndex, mPendingVibratorOffDeadline);
return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
}
}
diff --git a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
index 8585e3473ef3..fb5140d862b7 100644
--- a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
@@ -34,9 +34,9 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
private final boolean mCancelled;
CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
- VibratorController controller, long previousStepVibratorOffTimeout) {
+ VibratorController controller, long pendingVibratorOffDeadline) {
super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
- previousStepVibratorOffTimeout);
+ pendingVibratorOffDeadline);
mCancelled = cancelled;
}
@@ -73,10 +73,11 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
return VibrationStepConductor.EMPTY_STEP_LIST;
}
+ long now = SystemClock.uptimeMillis();
float currentAmplitude = controller.getCurrentAmplitude();
long remainingOnDuration =
- previousStepVibratorOffTimeout - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT
- - SystemClock.uptimeMillis();
+ mPendingVibratorOffDeadline - now
+ - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
long rampDownDuration =
Math.min(remainingOnDuration,
conductor.vibrationSettings.getRampDownDuration());
@@ -89,8 +90,10 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
stopVibrating();
return VibrationStepConductor.EMPTY_STEP_LIST;
} else {
+ // Vibration is completing normally, turn off after the deadline in case we
+ // don't receive the callback in time (callback also triggers it right away).
return Arrays.asList(new TurnOffVibratorStep(
- conductor, previousStepVibratorOffTimeout, controller));
+ conductor, mPendingVibratorOffDeadline, controller));
}
}
@@ -100,13 +103,18 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
+ " from amplitude " + currentAmplitude
+ " for " + rampDownDuration + "ms");
}
+
+ // If we are cancelling this vibration then make sure the vibrator will be turned off
+ // immediately after the ramp off duration. Otherwise, this is a planned ramp off for
+ // the remaining ON duration, then just propagate the mPendingVibratorOffDeadline so the
+ // turn off step will wait for the vibration completion callback and end gracefully.
+ long rampOffVibratorOffDeadline =
+ mCancelled ? (now + rampDownDuration) : mPendingVibratorOffDeadline;
float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
float amplitudeTarget = currentAmplitude - amplitudeDelta;
- long newVibratorOffTimeout =
- mCancelled ? rampDownDuration : previousStepVibratorOffTimeout;
return Arrays.asList(
new RampOffVibratorStep(conductor, startTime, amplitudeTarget, amplitudeDelta,
- controller, newVibratorOffTimeout));
+ controller, rampOffVibratorOffDeadline));
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index f8b99265246a..545ec5bcff03 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -40,11 +40,11 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
// This step should wait for the last vibration to finish (with the timeout) and for the
// intended step start time (to respect the effect delays).
- super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
- index, previousStepVibratorOffTimeout);
+ super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
+ index, pendingVibratorOffDeadline);
}
@Override
@@ -60,18 +60,22 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
if (primitives.isEmpty()) {
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
+ effect.getSegments().get(segmentIndex));
- return skipToNextSteps(/* segmentsSkipped= */ 1);
+ // Skip this step and play the next one right away.
+ return nextSteps(/* segmentsPlayed= */ 1);
}
if (VibrationThread.DEBUG) {
Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
- + controller.getVibratorInfo().getId());
+ + getVibratorId());
}
+
PrimitiveSegment[] primitivesArray =
primitives.toArray(new PrimitiveSegment[primitives.size()]);
- mVibratorOnResult = controller.on(primitivesArray, getVibration().id);
- getVibration().stats().reportComposePrimitives(mVibratorOnResult, primitivesArray);
+ long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
+ handleVibratorOnResult(vibratorOnResult);
+ getVibration().stats().reportComposePrimitives(vibratorOnResult, primitivesArray);
+ // The next start and off times will be calculated from mVibratorOnResult.
return nextSteps(/* segmentsPlayed= */ primitives.size());
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index 81f52c912f28..8bfa2c3cd082 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -41,11 +41,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
// This step should wait for the last vibration to finish (with the timeout) and for the
// intended step start time (to respect the effect delays).
- super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
- index, previousStepVibratorOffTimeout);
+ super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
+ index, pendingVibratorOffDeadline);
}
@Override
@@ -61,7 +61,8 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
if (pwles.isEmpty()) {
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
+ effect.getSegments().get(segmentIndex));
- return skipToNextSteps(/* segmentsSkipped= */ 1);
+ // Skip this step and play the next one right away.
+ return nextSteps(/* segmentsPlayed= */ 1);
}
if (VibrationThread.DEBUG) {
@@ -69,9 +70,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
+ controller.getVibratorInfo().getId());
}
RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
- mVibratorOnResult = controller.on(pwlesArray, getVibration().id);
- getVibration().stats().reportComposePwle(mVibratorOnResult, pwlesArray);
+ long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ handleVibratorOnResult(vibratorOnResult);
+ getVibration().stats().reportComposePwle(vibratorOnResult, pwlesArray);
+ // The next start and off times will be calculated from mVibratorOnResult.
return nextSteps(/* segmentsPlayed= */ pwles.size());
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index 41902147838d..d91bafa7c8c2 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -35,11 +35,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
// This step should wait for the last vibration to finish (with the timeout) and for the
// intended step start time (to respect the effect delays).
- super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
- index, previousStepVibratorOffTimeout);
+ super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
+ index, pendingVibratorOffDeadline);
}
@Override
@@ -50,7 +50,8 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
if (!(segment instanceof PrebakedSegment)) {
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a "
+ "PerformPrebakedVibratorStep: " + segment);
- return skipToNextSteps(/* segmentsSkipped= */ 1);
+ // Skip this step and play the next one right away.
+ return nextSteps(/* segmentsPlayed= */ 1);
}
PrebakedSegment prebaked = (PrebakedSegment) segment;
@@ -61,10 +62,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
}
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
- mVibratorOnResult = controller.on(prebaked, getVibration().id);
- getVibration().stats().reportPerformEffect(mVibratorOnResult, prebaked);
+ long vibratorOnResult = controller.on(prebaked, getVibration().id);
+ handleVibratorOnResult(vibratorOnResult);
+ getVibration().stats().reportPerformEffect(vibratorOnResult, prebaked);
- if (mVibratorOnResult == 0 && prebaked.shouldFallback()
+ if (vibratorOnResult == 0 && prebaked.shouldFallback()
&& (fallback instanceof VibrationEffect.Composed)) {
if (VibrationThread.DEBUG) {
Slog.d(VibrationThread.TAG, "Playing fallback for effect "
@@ -72,14 +74,15 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
}
AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
replaceCurrentSegment((VibrationEffect.Composed) fallback),
- segmentIndex, previousStepVibratorOffTimeout);
+ segmentIndex, mPendingVibratorOffDeadline);
List<Step> fallbackResult = fallbackStep.play();
// Update the result with the fallback result so this step is seamlessly
// replaced by the fallback to any outer application of this.
- mVibratorOnResult = fallbackStep.getVibratorOnDuration();
+ handleVibratorOnResult(fallbackStep.getVibratorOnDuration());
return fallbackResult;
}
+ // The next start and off times will be calculated from mVibratorOnResult.
return nextSteps(/* segmentsPlayed= */ 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
diff --git a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
index 8cf5fb394d9d..84da9f2c58ec 100644
--- a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
@@ -30,9 +30,9 @@ final class RampOffVibratorStep extends AbstractVibratorStep {
RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget,
float amplitudeDelta, VibratorController controller,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
- previousStepVibratorOffTimeout);
+ pendingVibratorOffDeadline);
mAmplitudeTarget = amplitudeTarget;
mAmplitudeDelta = amplitudeDelta;
}
@@ -68,15 +68,17 @@ final class RampOffVibratorStep extends AbstractVibratorStep {
float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
if (newAmplitudeTarget < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN) {
- // Vibrator amplitude cannot go further down, just turn it off.
+ // Vibrator amplitude cannot go further down, just turn it off with the configured
+ // deadline that has been adjusted for the scenario when this was triggered by a
+ // cancelled vibration.
return Arrays.asList(new TurnOffVibratorStep(
- conductor, previousStepVibratorOffTimeout, controller));
+ conductor, mPendingVibratorOffDeadline, controller));
}
return Arrays.asList(new RampOffVibratorStep(
conductor,
startTime + conductor.vibrationSettings.getRampStepDuration(),
newAmplitudeTarget, mAmplitudeDelta, controller,
- previousStepVibratorOffTimeout));
+ mPendingVibratorOffDeadline));
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index 6fb9111793ea..1672470f1f1a 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -39,26 +39,34 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
*/
private static final int REPEATING_EFFECT_ON_DURATION = 5000; // 5s
- private long mNextOffTime;
-
SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
VibratorController controller, VibrationEffect.Composed effect, int index,
- long previousStepVibratorOffTimeout) {
+ long pendingVibratorOffDeadline) {
// This step has a fixed startTime coming from the timings of the waveform it's playing.
- super(conductor, startTime, controller, effect, index, previousStepVibratorOffTimeout);
- mNextOffTime = previousStepVibratorOffTimeout;
+ super(conductor, startTime, controller, effect, index, pendingVibratorOffDeadline);
}
@Override
public boolean acceptVibratorCompleteCallback(int vibratorId) {
- if (controller.getVibratorInfo().getId() == vibratorId) {
- mVibratorCompleteCallbackReceived = true;
- mNextOffTime = SystemClock.uptimeMillis();
+ // Ensure the super method is called and will reset the off timeout and boolean flag.
+ // This is true if the vibrator was ON and this callback has the same vibratorId.
+ if (!super.acceptVibratorCompleteCallback(vibratorId)) {
+ return false;
}
+
// Timings are tightly controlled here, so only trigger this step if the vibrator was
// supposed to be ON but has completed prematurely, to turn it back on as soon as
- // possible.
- return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
+ // possible. If the vibrator turned off during a zero-amplitude step, just wait for
+ // the correct start time of this step before playing it.
+ boolean shouldAcceptCallback =
+ (SystemClock.uptimeMillis() < startTime) && (controller.getCurrentAmplitude() > 0);
+
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Amplitude step received completion callback from " + vibratorId
+ + ", accepted = " + shouldAcceptCallback);
+ }
+ return shouldAcceptCallback;
}
@Override
@@ -78,40 +86,38 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
if (mVibratorCompleteCallbackReceived && latency < 0) {
// This step was run early because the vibrator turned off prematurely.
// Turn it back on and return this same step to run at the exact right time.
- mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
+ turnVibratorBackOn(/* remainingDuration= */ -latency);
return Arrays.asList(new SetAmplitudeVibratorStep(conductor, startTime, controller,
- effect, segmentIndex, mNextOffTime));
+ effect, segmentIndex, mPendingVibratorOffDeadline));
}
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
if (!(segment instanceof StepSegment)) {
Slog.w(VibrationThread.TAG,
"Ignoring wrong segment for a SetAmplitudeVibratorStep: " + segment);
- return skipToNextSteps(/* segmentsSkipped= */ 1);
+ // Use original startTime to avoid propagating latencies to the waveform.
+ return nextSteps(startTime, /* segmentsPlayed= */ 1);
}
StepSegment stepSegment = (StepSegment) segment;
if (stepSegment.getDuration() == 0) {
- // Skip waveform entries with zero timing.
- return skipToNextSteps(/* segmentsSkipped= */ 1);
+ // Use original startTime to avoid propagating latencies to the waveform.
+ return nextSteps(startTime, /* segmentsPlayed= */ 1);
}
float amplitude = stepSegment.getAmplitude();
if (amplitude == 0) {
- if (previousStepVibratorOffTimeout > now) {
+ if (mPendingVibratorOffDeadline > now) {
// Amplitude cannot be set to zero, so stop the vibrator.
stopVibrating();
- mNextOffTime = now;
}
} else {
- if (startTime >= mNextOffTime) {
+ if (startTime >= mPendingVibratorOffDeadline) {
// Vibrator is OFF. Turn vibrator back on for the duration of another
// cycle before setting the amplitude.
long onDuration = getVibratorOnDuration(effect, segmentIndex);
if (onDuration > 0) {
- mVibratorOnResult = startVibrating(onDuration);
- mNextOffTime = now + onDuration
- + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+ startVibrating(onDuration);
}
}
changeAmplitude(amplitude);
@@ -119,27 +125,32 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
// Use original startTime to avoid propagating latencies to the waveform.
long nextStartTime = startTime + segment.getDuration();
- return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
+ return nextSteps(nextStartTime, /* segmentsPlayed= */ 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private long turnVibratorBackOn(long remainingDuration) {
+ private void turnVibratorBackOn(long remainingDuration) {
long onDuration = getVibratorOnDuration(effect, segmentIndex);
if (onDuration <= 0) {
// Vibrator is supposed to go back off when this step starts, so just leave it off.
- return previousStepVibratorOffTimeout;
+ return;
}
onDuration += remainingDuration;
+
+ if (VibrationThread.DEBUG) {
+ Slog.d(VibrationThread.TAG,
+ "Turning the vibrator back ON using the remaining duration of "
+ + remainingDuration + "ms, for a total of " + onDuration + "ms");
+ }
+
float expectedAmplitude = controller.getCurrentAmplitude();
- mVibratorOnResult = startVibrating(onDuration);
- if (mVibratorOnResult > 0) {
+ long vibratorOnResult = startVibrating(onDuration);
+ if (vibratorOnResult > 0) {
// Set the amplitude back to the value it was supposed to be playing at.
changeAmplitude(expectedAmplitude);
}
- return SystemClock.uptimeMillis() + onDuration
- + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
}
private long startVibrating(long duration) {
@@ -149,6 +160,7 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
+ duration + "ms");
}
long vibratorOnResult = controller.on(duration, getVibration().id);
+ handleVibratorOnResult(vibratorOnResult);
getVibration().stats().reportVibratorOn(vibratorOnResult);
return vibratorOnResult;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 0799b955b6f1..0af171871792 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -112,8 +112,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
@Nullable
AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int segmentIndex,
- long previousStepVibratorOffTimeout) {
+ VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
@@ -123,24 +122,24 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (segmentIndex < 0) {
// No more segments to play, last step is to complete the vibration on this vibrator.
return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false,
- controller, previousStepVibratorOffTimeout);
+ controller, pendingVibratorOffDeadline);
}
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
if (segment instanceof PrebakedSegment) {
return new PerformPrebakedVibratorStep(this, startTime, controller, effect,
- segmentIndex, previousStepVibratorOffTimeout);
+ segmentIndex, pendingVibratorOffDeadline);
}
if (segment instanceof PrimitiveSegment) {
return new ComposePrimitivesVibratorStep(this, startTime, controller, effect,
- segmentIndex, previousStepVibratorOffTimeout);
+ segmentIndex, pendingVibratorOffDeadline);
}
if (segment instanceof RampSegment) {
return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
- previousStepVibratorOffTimeout);
+ pendingVibratorOffDeadline);
}
return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
- previousStepVibratorOffTimeout);
+ pendingVibratorOffDeadline);
}
/** Called when this conductor is going to be started running by the VibrationThread. */
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 2f12a820eb81..d1cde602b391 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -387,8 +387,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* An internal-only version of vibrate that allows the caller access to the {@link Vibration}.
* The Vibration is only returned if it is ongoing after this method returns.
*/
- @Nullable
@VisibleForTesting
+ @Nullable
Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
@@ -1844,6 +1844,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
attrs, commonOptions.description, deathBinder);
if (vib != null && !commonOptions.background) {
try {
+ // Waits for the client vibration to finish, but the VibrationThread may still
+ // do cleanup after this.
vib.waitForEnd();
} catch (InterruptedException e) {
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 4153ea5cb936..c04b19558770 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1655,8 +1655,7 @@ final class AccessibilityController {
}
// If the window is completely covered by other windows - ignore.
- Region intersectionWindow = mTempRegion1;
- if (!intersectionWindow.op(unaccountedSpace, regionInScreen, Region.Op.INTERSECT)) {
+ if (unaccountedSpace.quickReject(regionInScreen)) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 765e7f08d201..f8f94f655a16 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -312,7 +312,6 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.InputApplicationHandle;
@@ -357,6 +356,7 @@ import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
+import com.android.server.wm.utils.WmDisplayCutout;
import dalvik.annotation.optimization.NeverCompile;
@@ -9684,9 +9684,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
- final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
- .getDisplayCutout();
- policy.getNonDecorInsetsLw(rotation, cutout, mNonDecorInsets[rotation]);
+ final WmDisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation);
+ policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
mStableInsets[rotation].set(mNonDecorInsets[rotation]);
policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 55d6b2fe8226..c940a658015d 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -375,9 +375,6 @@ public class AppTransition implements Dump {
final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
int redoLayout = notifyAppTransitionStartingLocked(
- AppTransition.isKeyguardGoingAwayTransitOld(transit),
- AppTransition.isKeyguardOccludeTransitOld(transit),
- topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
? topOpeningAnim.getStatusBarTransitionsStartTime()
: SystemClock.uptimeMillis(),
@@ -416,7 +413,7 @@ public class AppTransition implements Dump {
}
void freeze() {
- final boolean keyguardGoingAway = mNextAppTransitionRequests.contains(
+ final boolean keyguardGoingAwayCancelled = mNextAppTransitionRequests.contains(
TRANSIT_KEYGUARD_GOING_AWAY);
// The RemoteAnimationControl didn't register AppTransitionListener and
@@ -429,7 +426,7 @@ public class AppTransition implements Dump {
mNextAppTransitionRequests.clear();
clear();
setReady();
- notifyAppTransitionCancelledLocked(keyguardGoingAway);
+ notifyAppTransitionCancelledLocked(keyguardGoingAwayCancelled);
}
private void setAppTransitionState(int state) {
@@ -479,9 +476,9 @@ public class AppTransition implements Dump {
}
}
- private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway);
+ mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAwayCancelled);
}
}
@@ -491,14 +488,12 @@ public class AppTransition implements Dump {
}
}
- private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway,
- boolean keyguardOcclude, long duration, long statusBarAnimationStartTime,
+ private int notifyAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
int redoLayout = 0;
for (int i = 0; i < mListeners.size(); i++) {
- redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
- keyguardOcclude, duration, statusBarAnimationStartTime,
- statusBarAnimationDuration);
+ redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(
+ statusBarAnimationStartTime, statusBarAnimationDuration);
}
return redoLayout;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 44f388b6ed39..4b0005d77e40 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -20,10 +20,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
@@ -95,7 +91,6 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionOldType;
import android.view.WindowManager.TransitionType;
-import android.view.animation.Animation;
import android.window.ITaskFragmentOrganizer;
import com.android.internal.annotations.VisibleForTesting;
@@ -297,7 +292,6 @@ public class AppTransitionController {
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
- handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
} finally {
@@ -1171,30 +1165,6 @@ public class AppTransitionController {
}
}
- private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) {
- if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
- && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
- if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
- && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
- && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
- Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
- if (anim != null) {
- anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
- mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
- }
- }
- }
- if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
- && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
- mDisplayContent.startKeyguardExitOnNonAppWindows(
- transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
- }
- }
-
private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 9a94a4f54b61..d3452277a29f 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -22,6 +22,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
@@ -63,6 +64,15 @@ import java.util.ArrayList;
class BLASTSyncEngine {
private static final String TAG = "BLASTSyncEngine";
+ /** No specific method. Used by override specifiers. */
+ public static final int METHOD_UNDEFINED = -1;
+
+ /** No sync method. Apps will draw/present internally and just report. */
+ public static final int METHOD_NONE = 0;
+
+ /** Sync with BLAST. Apps will draw and then send the buffer to be applied in sync. */
+ public static final int METHOD_BLAST = 1;
+
interface TransactionReadyListener {
void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
}
@@ -85,6 +95,7 @@ class BLASTSyncEngine {
*/
class SyncGroup {
final int mSyncId;
+ final int mSyncMethod;
final TransactionReadyListener mListener;
final Runnable mOnTimeout;
boolean mReady = false;
@@ -92,8 +103,9 @@ class BLASTSyncEngine {
private SurfaceControl.Transaction mOrphanTransaction = null;
private String mTraceName;
- private SyncGroup(TransactionReadyListener listener, int id, String name) {
+ private SyncGroup(TransactionReadyListener listener, int id, String name, int method) {
mSyncId = id;
+ mSyncMethod = method;
mListener = listener;
mOnTimeout = () -> {
Slog.w(TAG, "Sync group " + mSyncId + " timeout");
@@ -271,16 +283,13 @@ class BLASTSyncEngine {
* Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet}
* before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}.
*/
- SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) {
- return new SyncGroup(listener, mNextSyncId++, name);
+ SyncGroup prepareSyncSet(TransactionReadyListener listener, String name, int method) {
+ return new SyncGroup(listener, mNextSyncId++, name, method);
}
- int startSyncSet(TransactionReadyListener listener) {
- return startSyncSet(listener, BLAST_TIMEOUT_DURATION, "");
- }
-
- int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name) {
- final SyncGroup s = prepareSyncSet(listener, name);
+ int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
+ int method) {
+ final SyncGroup s = prepareSyncSet(listener, name, method);
startSyncSet(s, timeoutMs);
return s.mSyncId;
}
@@ -302,6 +311,11 @@ class BLASTSyncEngine {
scheduleTimeout(s, timeoutMs);
}
+ @Nullable
+ SyncGroup getSyncSet(int id) {
+ return mActiveSyncs.get(id);
+ }
+
boolean hasActiveSync() {
return mActiveSyncs.size() != 0;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8a34af3b0107..1c90bbae79ec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -441,7 +441,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
- /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
+ /** @see #computeCompatSmallestWidth(boolean, int, int) */
private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
/**
@@ -2017,7 +2017,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
// By updating the Display info here it will be available to
// #computeScreenConfiguration() later.
- updateDisplayAndOrientation(getConfiguration().uiMode, null /* outConfig */);
+ updateDisplayAndOrientation(null /* outConfig */);
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
@@ -2067,7 +2067,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* changed.
* Do not call if {@link WindowManagerService#mDisplayReady} == false.
*/
- private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
+ private DisplayInfo updateDisplayAndOrientation(Configuration outConfig) {
// Use the effective "visual" dimensions based on current rotation
final int rotation = getRotation();
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
@@ -2079,18 +2079,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
- final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
- displayCutout);
+ final Rect appFrame = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
+ wmDisplayCutout);
mDisplayInfo.rotation = rotation;
mDisplayInfo.logicalWidth = dw;
mDisplayInfo.logicalHeight = dh;
mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;
mDisplayInfo.physicalXDpi = mBaseDisplayPhysicalXDpi;
mDisplayInfo.physicalYDpi = mBaseDisplayPhysicalYDpi;
- mDisplayInfo.appWidth = appWidth;
- mDisplayInfo.appHeight = appHeight;
+ mDisplayInfo.appWidth = appFrame.width();
+ mDisplayInfo.appHeight = appFrame.height();
if (isDefaultDisplay) {
mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
@@ -2104,7 +2102,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
- computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, uiMode, dw, dh,
+ computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, dw, dh,
mDisplayMetrics.density, outConfig);
mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
@@ -2194,10 +2192,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);
outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());
- final int uiMode = getConfiguration().uiMode;
- final DisplayCutout displayCutout =
- calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
- computeScreenAppConfiguration(outConfig, dw, dh, rotation, uiMode, displayCutout);
+ final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+ computeScreenAppConfiguration(outConfig, dw, dh, rotation, wmDisplayCutout);
final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
displayInfo.rotation = rotation;
@@ -2206,38 +2202,35 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final Rect appBounds = outConfig.windowConfiguration.getAppBounds();
displayInfo.appWidth = appBounds.width();
displayInfo.appHeight = appBounds.height();
+ final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
displayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
- computeSizeRangesAndScreenLayout(displayInfo, rotated, uiMode, dw, dh,
+ computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh,
mDisplayMetrics.density, outConfig);
return displayInfo;
}
/** Compute configuration related to application without changing current display. */
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
- int rotation, int uiMode, DisplayCutout displayCutout) {
- final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
- displayCutout);
- mDisplayPolicy.getNonDecorInsetsLw(rotation, displayCutout, mTmpRect);
- final int leftInset = mTmpRect.left;
- final int topInset = mTmpRect.top;
+ int rotation, WmDisplayCutout wmDisplayCutout) {
+ final DisplayFrames displayFrames =
+ mDisplayPolicy.getSimulatedDisplayFrames(rotation, dw, dh, wmDisplayCutout);
+ final Rect appFrame =
+ mDisplayPolicy.getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
// AppBounds at the root level should mirror the app screen size.
- outConfig.windowConfiguration.setAppBounds(leftInset /* left */, topInset /* top */,
- leftInset + appWidth /* right */, topInset + appHeight /* bottom */);
+ outConfig.windowConfiguration.setAppBounds(appFrame);
outConfig.windowConfiguration.setRotation(rotation);
outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
final float density = mDisplayMetrics.density;
- outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation,
- uiMode, displayCutout) / density + 0.5f);
- outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation,
- uiMode, displayCutout) / density + 0.5f);
+ final Point configSize =
+ mDisplayPolicy.getConfigDisplaySizeWithSimulatedFrame(displayFrames);
+ outConfig.screenWidthDp = (int) (configSize.x / density + 0.5f);
+ outConfig.screenHeightDp = (int) (configSize.y / density + 0.5f);
outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw, dh);
+ outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
outConfig.windowConfiguration.setDisplayRotation(rotation);
}
@@ -2246,7 +2239,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Do not call if mDisplayReady == false.
*/
void computeScreenConfiguration(Configuration config) {
- final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
+ final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
mTmpRect.set(0, 0, dw, dh);
@@ -2255,8 +2248,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
config.windowConfiguration.setWindowingMode(getWindowingMode());
config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
- computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
- displayInfo.displayCutout);
+ computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation,
+ calculateDisplayCutoutForRotation(getRotation()));
config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
| ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -2345,7 +2338,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
- private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
+ private int computeCompatSmallestWidth(boolean rotated, int dw, int dh) {
mTmpDisplayMetrics.setTo(mDisplayMetrics);
final DisplayMetrics tmpDm = mTmpDisplayMetrics;
final int unrotDw, unrotDh;
@@ -2356,25 +2349,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
unrotDw = dw;
unrotDh = dh;
}
- int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
- unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
- unrotDw);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
- unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
- unrotDw);
+ int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw);
return sw;
}
- private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
+ private int reduceCompatConfigWidthSize(int curSize, int rotation,
DisplayMetrics dm, int dw, int dh) {
- final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
- rotation).getDisplayCutout();
- dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
- displayCutout);
+ final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+ final Rect nonDecorSize = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
+ wmDisplayCutout);
+ dm.noncompatWidthPixels = nonDecorSize.width();
+ dm.noncompatHeightPixels = nonDecorSize.height();
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
if (curSize == 0 || size < curSize) {
@@ -2384,7 +2372,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
- int uiMode, int dw, int dh, float density, Configuration outConfig) {
+ int dw, int dh, float density, Configuration outConfig) {
// We need to determine the smallest width that will occur under normal
// operation. To this, start with the base screen size and compute the
@@ -2402,37 +2390,34 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
displayInfo.smallestNominalAppHeight = 1<<30;
displayInfo.largestNominalAppWidth = 0;
displayInfo.largestNominalAppHeight = 0;
- adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh);
- adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
- adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
- adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh);
+ adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw);
if (outConfig == null) {
return;
}
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
- sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
- sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
- sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
outConfig.smallestScreenWidthDp =
(int) (displayInfo.smallestNominalAppWidth / density + 0.5f);
outConfig.screenLayout = sl;
}
- private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
- int uiMode) {
+ private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh) {
// Get the display cutout at this rotation.
- final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
- rotation).getDisplayCutout();
+ final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
// Get the app screen size at this rotation.
- int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
- int h = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation, displayCutout);
+ final Rect size = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation, wmDisplayCutout);
// Compute the screen layout size class for this rotation.
- int longSize = w;
- int shortSize = h;
+ int longSize = size.width();
+ int shortSize = size.height();
if (longSize < shortSize) {
int tmp = longSize;
longSize = shortSize;
@@ -2443,25 +2428,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
}
- private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation,
- int uiMode, int dw, int dh) {
- final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
- rotation).getDisplayCutout();
- final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
- displayCutout);
- if (width < displayInfo.smallestNominalAppWidth) {
- displayInfo.smallestNominalAppWidth = width;
+ private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
+ final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+ final Point size = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, wmDisplayCutout);
+ if (size.x < displayInfo.smallestNominalAppWidth) {
+ displayInfo.smallestNominalAppWidth = size.x;
}
- if (width > displayInfo.largestNominalAppWidth) {
- displayInfo.largestNominalAppWidth = width;
+ if (size.x > displayInfo.largestNominalAppWidth) {
+ displayInfo.largestNominalAppWidth = size.x;
}
- final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
- displayCutout);
- if (height < displayInfo.smallestNominalAppHeight) {
- displayInfo.smallestNominalAppHeight = height;
+ if (size.y < displayInfo.smallestNominalAppHeight) {
+ displayInfo.smallestNominalAppHeight = size.y;
}
- if (height > displayInfo.largestNominalAppHeight) {
- displayInfo.largestNominalAppHeight = height;
+ if (size.y > displayInfo.largestNominalAppHeight) {
+ displayInfo.largestNominalAppHeight = size.y;
}
}
@@ -3316,6 +3296,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mAsyncRotationController.keepAppearanceInPreviousRotation();
}
} else if (isRotationChanging()) {
+ if (displayChange != null) {
+ final boolean seamless = mDisplayRotation.shouldRotateSeamlessly(
+ displayChange.getStartRotation(), displayChange.getEndRotation(),
+ false /* forceUpdate */);
+ if (seamless) {
+ t.onSeamlessRotating(this);
+ }
+ }
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
@@ -6598,7 +6586,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
// It is only needed when freezing display in legacy transition.
if (mTransitionController.isShellTransitionsEnabled()) return;
continueUpdateOrientationForDiffOrienLaunchingApp();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1a34c93f2ad6..508e6dc77a61 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -19,15 +19,19 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.TYPE_INTERNAL;
+import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -103,6 +107,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.gui.DropInputMode;
@@ -127,6 +132,8 @@ import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
+import android.view.PrivacyIndicatorBounds;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
@@ -136,7 +143,6 @@ import android.view.WindowLayout;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
-import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityManager;
import android.window.ClientWindowFrames;
@@ -160,6 +166,7 @@ import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -383,6 +390,16 @@ public class DisplayPolicy {
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+ // TODO (b/235842600): Use public type once we can treat task bar as navigation bar.
+ private static final int[] STABLE_TYPES = new int[]{
+ ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT, ITYPE_BOTTOM_DISPLAY_CUTOUT,
+ ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR
+ };
+ private static final int[] NON_DECOR_TYPES = new int[]{
+ ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT, ITYPE_BOTTOM_DISPLAY_CUTOUT,
+ ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR
+ };
+
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
@@ -602,9 +619,8 @@ public class DisplayPolicy {
}
@Override
- public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
- boolean keyguardOccluding, long duration,
- long statusBarAnimationStartTime, long statusBarAnimationDuration) {
+ public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+ long statusBarAnimationDuration) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
@@ -616,7 +632,7 @@ public class DisplayPolicy {
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
mHandler.post(mAppTransitionCancelled);
}
@@ -2007,35 +2023,6 @@ public class DisplayPolicy {
return mUiContext;
}
- private int getNavigationBarWidth(int rotation, int uiMode, int position) {
- if (mNavigationBar == null) {
- return 0;
- }
- LayoutParams lp = mNavigationBar.mAttrs;
- if (lp.paramsForRotation != null
- && lp.paramsForRotation.length == 4
- && lp.paramsForRotation[rotation] != null) {
- lp = lp.paramsForRotation[rotation];
- }
- Insets providedInsetsSize = null;
- if (lp.providedInsets != null) {
- for (InsetsFrameProvider provider : lp.providedInsets) {
- if (provider.type != ITYPE_NAVIGATION_BAR) {
- continue;
- }
- providedInsetsSize = provider.insetsSize;
- }
- }
- if (providedInsetsSize != null) {
- if (position == NAV_BAR_LEFT) {
- return providedInsetsSize.left;
- } else if (position == NAV_BAR_RIGHT) {
- return providedInsetsSize.right;
- }
- }
- return lp.width;
- }
-
@VisibleForTesting
void setCanSystemBarsBeShownByUser(boolean canBeShown) {
mCanSystemBarsBeShownByUser = canBeShown;
@@ -2057,45 +2044,24 @@ public class DisplayPolicy {
}
/**
- * Return the display width available after excluding any screen
- * decorations that could never be removed in Honeycomb. That is, system bar or
- * button bar.
+ * Return the display frame available after excluding any screen decorations that could never be
+ * removed in Honeycomb. That is, system bar or button bar.
+ *
+ * @return display frame excluding all non-decor insets.
*/
- public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
- int width = fullWidth;
- if (hasNavigationBar()) {
- final int navBarPosition = navigationBarPosition(rotation);
- if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
- width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
- }
- }
- if (displayCutout != null) {
- width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
- }
- return width;
+ Rect getNonDecorDisplayFrame(int fullWidth, int fullHeight, int rotation,
+ WmDisplayCutout cutout) {
+ final DisplayFrames displayFrames =
+ getSimulatedDisplayFrames(rotation, fullWidth, fullHeight, cutout);
+ return getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
}
- @VisibleForTesting
- int getNavigationBarHeight(int rotation) {
- if (mNavigationBar == null) {
- return 0;
- }
- LayoutParams lp = mNavigationBar.mAttrs.forRotation(rotation);
- Insets providedInsetsSize = null;
- if (lp.providedInsets != null) {
- for (InsetsFrameProvider provider : lp.providedInsets) {
- if (provider.type != ITYPE_NAVIGATION_BAR) {
- continue;
- }
- providedInsetsSize = provider.insetsSize;
- if (providedInsetsSize != null) {
- return providedInsetsSize.bottom;
- }
- break;
- }
- }
- return lp.height;
+ Rect getNonDecorDisplayFrameWithSimulatedFrame(DisplayFrames displayFrames) {
+ final Rect nonDecorInsets =
+ getInsetsWithInternalTypes(displayFrames, NON_DECOR_TYPES).toRect();
+ final Rect displayFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
+ displayFrame.inset(nonDecorInsets);
+ return displayFrame;
}
/**
@@ -2117,53 +2083,23 @@ public class DisplayPolicy {
}
/**
- * Return the display height available after excluding any screen
- * decorations that could never be removed in Honeycomb. That is, system bar or
- * button bar.
- */
- public int getNonDecorDisplayHeight(int fullHeight, int rotation, DisplayCutout displayCutout) {
- int height = fullHeight;
- final int navBarPosition = navigationBarPosition(rotation);
- if (navBarPosition == NAV_BAR_BOTTOM) {
- height -= getNavigationBarHeight(rotation);
- }
- if (displayCutout != null) {
- height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
- }
- return height;
- }
-
- /**
- * Return the available screen width that we should report for the
+ * Return the available screen size that we should report for the
* configuration. This must be no larger than
- * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+ * {@link #getNonDecorDisplayFrame(int, int, int, DisplayCutout)}; it may be smaller
* than that to account for more transient decoration like a status bar.
*/
- public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
- return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
+ public Point getConfigDisplaySize(int fullWidth, int fullHeight, int rotation,
+ WmDisplayCutout wmDisplayCutout) {
+ final DisplayFrames displayFrames = getSimulatedDisplayFrames(rotation, fullWidth,
+ fullHeight, wmDisplayCutout);
+ return getConfigDisplaySizeWithSimulatedFrame(displayFrames);
}
- /**
- * Return the available screen height that we should report for the
- * configuration. This must be no larger than
- * {@link #getNonDecorDisplayHeight(int, int, DisplayCutout)}; it may be smaller
- * than that to account for more transient decoration like a status bar.
- */
- public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
- // There is a separate status bar at the top of the display. We don't count that as part
- // of the fixed decor, since it can hide; however, for purposes of configurations,
- // we do want to exclude it since applications can't generally use that part
- // of the screen.
- int statusBarHeight = mStatusBarHeightForRotation[rotation];
- if (displayCutout != null) {
- // If there is a cutout, it may already have accounted for some part of the status
- // bar height.
- statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
- }
- return getNonDecorDisplayHeight(fullHeight, rotation, displayCutout)
- - statusBarHeight;
+ Point getConfigDisplaySizeWithSimulatedFrame(DisplayFrames displayFrames) {
+ final Insets insets = getInsetsWithInternalTypes(displayFrames, STABLE_TYPES);
+ Rect configFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
+ configFrame.inset(insets);
+ return new Point(configFrame.width(), configFrame.height());
}
/**
@@ -2195,48 +2131,75 @@ public class DisplayPolicy {
* Calculates the stable insets without running a layout.
*
* @param displayRotation the current display rotation
+ * @param displayWidth full display width
+ * @param displayHeight full display height
* @param displayCutout the current display cutout
* @param outInsets the insets to return
*/
- public void getStableInsetsLw(int displayRotation, DisplayCutout displayCutout,
- Rect outInsets) {
- outInsets.setEmpty();
+ public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+ WmDisplayCutout displayCutout, Rect outInsets) {
+ final DisplayFrames displayFrames = getSimulatedDisplayFrames(displayRotation,
+ displayWidth, displayHeight, displayCutout);
+ getStableInsetsWithSimulatedFrame(displayFrames, outInsets);
+ }
- // Navigation bar and status bar.
- getNonDecorInsetsLw(displayRotation, displayCutout, outInsets);
- convertNonDecorInsetsToStableInsets(outInsets, displayRotation);
+ void getStableInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
+ // Navigation bar, status bar, and cutout.
+ outInsets.set(getInsetsWithInternalTypes(displayFrames, STABLE_TYPES).toRect());
}
/**
* Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
- * bar or button bar. See {@link #getNonDecorDisplayWidth}.
- * @param displayRotation the current display rotation
- * @param displayCutout the current display cutout
+ * bar or button bar. See {@link #getNonDecorDisplayFrame}.
+ *
+ * @param displayRotation the current display rotation
+ * @param fullWidth the width of the display, including all insets
+ * @param fullHeight the height of the display, including all insets
+ * @param cutout the current display cutout
* @param outInsets the insets to return
*/
- public void getNonDecorInsetsLw(int displayRotation, DisplayCutout displayCutout,
- Rect outInsets) {
- outInsets.setEmpty();
-
- // Only navigation bar
- if (hasNavigationBar()) {
- final int uiMode = mService.mPolicy.getUiMode();
- int position = navigationBarPosition(displayRotation);
- if (position == NAV_BAR_BOTTOM) {
- outInsets.bottom = getNavigationBarHeight(displayRotation);
- } else if (position == NAV_BAR_RIGHT) {
- outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
- } else if (position == NAV_BAR_LEFT) {
- outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
- }
- }
+ public void getNonDecorInsetsLw(int displayRotation, int fullWidth, int fullHeight,
+ WmDisplayCutout cutout, Rect outInsets) {
+ final DisplayFrames displayFrames =
+ getSimulatedDisplayFrames(displayRotation, fullWidth, fullHeight, cutout);
+ getNonDecorInsetsWithSimulatedFrame(displayFrames, outInsets);
+ }
+
+ void getNonDecorInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
+ outInsets.set(getInsetsWithInternalTypes(displayFrames, NON_DECOR_TYPES).toRect());
+ }
+
+ DisplayFrames getSimulatedDisplayFrames(int displayRotation, int fullWidth,
+ int fullHeight, WmDisplayCutout cutout) {
+ final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
+ info.rotation = displayRotation;
+ info.logicalWidth = fullWidth;
+ info.logicalHeight = fullHeight;
+ info.displayCutout = cutout.getDisplayCutout();
+ final RoundedCorners roundedCorners =
+ mDisplayContent.calculateRoundedCornersForRotation(displayRotation);
+ final PrivacyIndicatorBounds indicatorBounds =
+ mDisplayContent.calculatePrivacyIndicatorBoundsForRotation(displayRotation);
+ final DisplayFrames displayFrames = new DisplayFrames(getDisplayId(), new InsetsState(),
+ info, cutout, roundedCorners, indicatorBounds);
+ simulateLayoutDisplay(displayFrames);
+ return displayFrames;
+ }
- if (displayCutout != null) {
- outInsets.left += displayCutout.getSafeInsetLeft();
- outInsets.top += displayCutout.getSafeInsetTop();
- outInsets.right += displayCutout.getSafeInsetRight();
- outInsets.bottom += displayCutout.getSafeInsetBottom();
- }
+ @VisibleForTesting
+ Insets getInsets(DisplayFrames displayFrames, @InsetsType int type) {
+ final InsetsState state = displayFrames.mInsetsState;
+ final Insets insets = state.calculateInsets(state.getDisplayFrame(), type,
+ true /* ignoreVisibility */);
+ return insets;
+ }
+
+ Insets getInsetsWithInternalTypes(DisplayFrames displayFrames,
+ @InternalInsetsType int[] types) {
+ final InsetsState state = displayFrames.mInsetsState;
+ final Insets insets = state.calculateInsetsWithInternalTypes(state.getDisplayFrame(), types,
+ true /* ignoreVisibility */);
+ return insets;
}
@NavigationBarPosition
@@ -2256,17 +2219,6 @@ public class DisplayPolicy {
}
/**
- * @return The side of the screen where navigation bar is positioned.
- * @see WindowManagerPolicyConstants#NAV_BAR_LEFT
- * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT
- * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM
- */
- @NavigationBarPosition
- public int getNavBarPosition() {
- return mNavigationBarPosition;
- }
-
- /**
* A new window has been focused.
*/
public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 5b702eac7059..7bb57d827a43 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -161,15 +161,14 @@ public class RecentsAnimationController implements DeathRecipient {
*/
final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
- boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
continueDeferredCancel();
return 0;
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
continueDeferredCancel();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 384a4d17c752..e38f5fe61888 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1883,8 +1883,7 @@ class Task extends TaskFragment {
}
final int newWinMode = getWindowingMode();
- if ((prevWinMode != newWinMode) && (mDisplayContent != null)
- && shouldStartChangeTransition(prevWinMode, newWinMode)) {
+ if (shouldStartChangeTransition(prevWinMode, mTmpPrevBounds)) {
initializeChangeTransition(mTmpPrevBounds);
}
@@ -2135,10 +2134,16 @@ class Task extends TaskFragment {
bounds.offset(horizontalDiff, verticalDiff);
}
- private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ private boolean shouldStartChangeTransition(int prevWinMode, @NonNull Rect prevBounds) {
if (!isLeafTask() || !canStartChangeTransition()) {
return false;
}
+ final int newWinMode = getWindowingMode();
+ if (mTransitionController.inTransition(this)) {
+ final Rect newBounds = getConfiguration().windowConfiguration.getBounds();
+ return prevWinMode != newWinMode || prevBounds.width() != newBounds.width()
+ || prevBounds.height() != newBounds.height();
+ }
// Only do an animation into and out-of freeform mode for now. Other mode
// transition animations are currently handled by system-ui.
return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 1b7c6d25f9c6..8872b3526e8c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -98,6 +98,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.am.HostingRecord;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.wm.utils.WmDisplayCutout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -2208,11 +2209,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation,
- displayInfo.displayCutout, mTmpInsets);
+ final WmDisplayCutout cutout =
+ rootTask.mDisplayContent.calculateDisplayCutoutForRotation(displayInfo.rotation);
+ final DisplayFrames displayFrames = policy.getSimulatedDisplayFrames(displayInfo.rotation,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, cutout);
+ policy.getNonDecorInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
- policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+ policy.getStableInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2d3e437bed60..c586b155a222 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -31,13 +32,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -68,11 +63,11 @@ import android.app.ActivityManager;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -80,7 +75,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.view.animation.Animation;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -205,6 +199,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** @see #setCanPipOnFinish */
private boolean mCanPipOnFinish = true;
+ private boolean mIsSeamlessRotation = false;
+ private IContainerFreezer mContainerFreezer = null;
+
Transition(@TransitionType int type, @TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
mType = type;
@@ -265,10 +262,31 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return mTargetDisplays.contains(dc);
}
+ /** Set a transition to be a seamless-rotation. */
void setSeamlessRotation(@NonNull WindowContainer wc) {
final ChangeInfo info = mChanges.get(wc);
if (info == null) return;
info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
+ onSeamlessRotating(wc.getDisplayContent());
+ }
+
+ /**
+ * Called when it's been determined that this is transition is a seamless rotation. This should
+ * be called before any WM changes have happened.
+ */
+ void onSeamlessRotating(@NonNull DisplayContent dc) {
+ // Don't need to do anything special if everything is using BLAST sync already.
+ if (mSyncEngine.getSyncSet(mSyncId).mSyncMethod == BLASTSyncEngine.METHOD_BLAST) return;
+ if (mContainerFreezer == null) {
+ mContainerFreezer = new ScreenshotFreezer();
+ }
+ mIsSeamlessRotation = true;
+ final WindowState top = dc.getDisplayPolicy().getTopFullscreenOpaqueWindow();
+ if (top != null) {
+ top.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Override sync-method for %s "
+ + "because seamless rotating", top.getName());
+ }
}
/**
@@ -285,6 +303,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
+ /** Only for testing. */
+ void setContainerFreezer(IContainerFreezer freezer) {
+ mContainerFreezer = freezer;
+ }
+
@TransitionState
int getState() {
return mState;
@@ -314,13 +337,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return mState == STATE_COLLECTING || mState == STATE_STARTED;
}
- /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
+ @VisibleForTesting
void startCollecting(long timeoutMs) {
+ startCollecting(timeoutMs, TransitionController.SYNC_METHOD);
+ }
+
+ /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
+ void startCollecting(long timeoutMs, int method) {
if (mState != STATE_PENDING) {
throw new IllegalStateException("Attempting to re-use a transition");
}
mState = STATE_COLLECTING;
- mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
+ mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG, method);
mController.mTransitionTracer.logState(this);
}
@@ -415,6 +443,37 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/**
+ * Records that a particular container is changing visibly (ie. something about it is changing
+ * while it remains visible). This only effects windows that are already in the collecting
+ * transition.
+ */
+ void collectVisibleChange(WindowContainer wc) {
+ if (mSyncEngine.getSyncSet(mSyncId).mSyncMethod == BLASTSyncEngine.METHOD_BLAST) {
+ // All windows are synced already.
+ return;
+ }
+ if (!isInTransition(wc)) return;
+
+ if (mContainerFreezer == null) {
+ mContainerFreezer = new ScreenshotFreezer();
+ }
+ Transition.ChangeInfo change = mChanges.get(wc);
+ if (change == null || !change.mVisible || !wc.isVisibleRequested()) return;
+ // Note: many more tests have already been done by caller.
+ mContainerFreezer.freeze(wc, change.mAbsoluteBounds);
+ }
+
+ /**
+ * @return {@code true} if `wc` is a participant or is a descendant of one.
+ */
+ boolean isInTransition(WindowContainer wc) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mParticipants.contains(p)) return true;
+ }
+ return false;
+ }
+
+ /**
* Specifies configuration change explicitly for the window container, so it can be chosen as
* transition target. This is usually used with transition mode
* {@link android.view.WindowManager#TRANSIT_CHANGE}.
@@ -531,6 +590,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
displays.add(target.getDisplayContent());
}
}
+ // Remove screenshot layers if necessary
+ if (mContainerFreezer != null) {
+ mContainerFreezer.cleanUp(t);
+ }
// Need to update layers on involved displays since they were all paused while
// the animation played. This puts the layers back into the correct order.
mController.mBuildingFinishLayers = true;
@@ -817,6 +880,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
transaction);
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
+ if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
+ for (int i = 0; i < mTargets.size(); ++i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ final ActivityRecord ar = mTargets.get(i).asActivityRecord();
+ if (ar == null || c.getMode() != TRANSIT_OPEN) continue;
+ int flags = c.getFlags();
+ flags |= ar.mUserId == ar.mWmService.mCurrentUserId
+ ? TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL
+ : TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
+ c.setFlags(flags);
+ break;
+ }
+ }
}
// TODO(b/188669821): Move to animation impl in shell.
@@ -1066,36 +1142,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
@TransitionType int transit, @TransitionFlags int flags) {
- if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
- || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
- && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
- if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
- && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
- && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
- Animation anim = mController.mAtm.mWindowManager.mPolicy
- .createKeyguardWallpaperExit(
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
- if (anim != null) {
- anim.scaleCurrentDuration(
- mController.mAtm.mWindowManager.getTransitionAnimationScaleLocked());
- dc.mWallpaperController.startWallpaperAnimation(anim);
- }
- }
- dc.startKeyguardExitOnNonAppWindows(
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
- (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
- if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
- // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
- // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
- // need to call IKeyguardService#keyguardGoingAway here.
- mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(
- SystemClock.uptimeMillis(), 0 /* duration */);
- }
- }
if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(
- true /* keyguardOccludingStarted */);
+ false /* notify */);
}
}
@@ -1982,4 +2031,111 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return sortedTargets;
}
}
+
+ /**
+ * Interface for freezing a container's content during sync preparation. Really just one impl
+ * but broken into an interface for testing (since you can't take screenshots in unit tests).
+ */
+ interface IContainerFreezer {
+ /**
+ * Makes sure a particular window is "frozen" for the remainder of a sync.
+ *
+ * @return whether the freeze was successful. It fails if `wc` is already in a frozen window
+ * or is not visible/ready.
+ */
+ boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds);
+
+ /** Populates `t` with operations that clean-up any state created to set-up the freeze. */
+ void cleanUp(SurfaceControl.Transaction t);
+ }
+
+ /**
+ * Freezes container content by taking a screenshot. Because screenshots are heavy, usage of
+ * any container "freeze" is currently explicit. WM code needs to be prudent about which
+ * containers to freeze.
+ */
+ @VisibleForTesting
+ private class ScreenshotFreezer implements IContainerFreezer {
+ /** Values are the screenshot "surfaces" or null if it was frozen via BLAST override. */
+ private final ArrayMap<WindowContainer, SurfaceControl> mSnapshots = new ArrayMap<>();
+
+ /** Takes a screenshot and puts it at the top of the container's surface. */
+ @Override
+ public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
+ if (!wc.isVisibleRequested()) return false;
+
+ // Check if any parents have already been "frozen". If so, `wc` is already part of that
+ // snapshot, so just skip it.
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mSnapshots.containsKey(p)) return false;
+ }
+
+ if (mIsSeamlessRotation) {
+ WindowState top = wc.getDisplayContent() == null ? null
+ : wc.getDisplayContent().getDisplayPolicy().getTopFullscreenOpaqueWindow();
+ if (top != null && (top == wc || top.isDescendantOf(wc))) {
+ // Don't use screenshots for seamless windows: these will use BLAST even if not
+ // BLAST mode.
+ mSnapshots.put(wc, null);
+ return true;
+ }
+ }
+
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Screenshotting %s [%s]",
+ wc.toString(), bounds.toString());
+
+ Rect cropBounds = new Rect(bounds);
+ cropBounds.offsetTo(0, 0);
+ SurfaceControl.LayerCaptureArgs captureArgs =
+ new SurfaceControl.LayerCaptureArgs.Builder(wc.getSurfaceControl())
+ .setSourceCrop(cropBounds)
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(captureArgs);
+ final HardwareBuffer buffer = screenshotBuffer == null ? null
+ : screenshotBuffer.getHardwareBuffer();
+ if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ // This can happen when display is not ready.
+ Slog.w(TAG, "Failed to capture screenshot for " + wc);
+ return false;
+ }
+ SurfaceControl snapshotSurface = wc.makeAnimationLeash()
+ .setName("transition snapshot: " + wc.toString())
+ .setOpaque(true)
+ .setParent(wc.getSurfaceControl())
+ .setSecure(screenshotBuffer.containsSecureLayers())
+ .setCallsite("Transition.ScreenshotSync")
+ .setBLASTLayer()
+ .build();
+ mSnapshots.put(wc, snapshotSurface);
+ SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();
+
+ t.setBuffer(snapshotSurface, buffer);
+ t.setDataSpace(snapshotSurface, screenshotBuffer.getColorSpace().getDataSpace());
+ t.show(snapshotSurface);
+
+ // Place it on top of anything else in the container.
+ t.setLayer(snapshotSurface, Integer.MAX_VALUE);
+ t.apply();
+ t.close();
+
+ // Detach the screenshot on the sync transaction (the screenshot is just meant to
+ // freeze the window until the sync transaction is applied (with all its other
+ // corresponding changes), so this is how we unfreeze it.
+ wc.getSyncTransaction().reparent(snapshotSurface, null /* newParent */);
+ return true;
+ }
+
+ @Override
+ public void cleanUp(SurfaceControl.Transaction t) {
+ for (int i = 0; i < mSnapshots.size(); ++i) {
+ SurfaceControl snap = mSnapshots.valueAt(i);
+ // May be null if it was frozen via BLAST override.
+ if (snap == null) continue;
+ t.remove(snap);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 846aa3e3739a..e2438097bce4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -46,6 +46,7 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
@@ -64,6 +65,11 @@ class TransitionController {
private static final boolean SHELL_TRANSITIONS_ROTATION =
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
+ /** Which sync method to use for transition syncs. */
+ static final int SYNC_METHOD =
+ android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", true)
+ ? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE;
+
/** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
private static final int DEFAULT_TIMEOUT_MS = 5000;
/** Less duration for CHANGE type because it does not involve app startup. */
@@ -160,6 +166,12 @@ class TransitionController {
/** Starts Collecting */
void moveToCollecting(@NonNull Transition transition) {
+ moveToCollecting(transition, SYNC_METHOD);
+ }
+
+ /** Starts Collecting */
+ @VisibleForTesting
+ void moveToCollecting(@NonNull Transition transition, int method) {
if (mCollectingTransition != null) {
throw new IllegalStateException("Simultaneous transition collection not supported.");
}
@@ -167,7 +179,7 @@ class TransitionController {
// Distinguish change type because the response time is usually expected to be not too long.
final long timeoutMs =
transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
- mCollectingTransition.startCollecting(timeoutMs);
+ mCollectingTransition.startCollecting(timeoutMs, method);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
mCollectingTransition);
dispatchLegacyAppTransitionPending();
@@ -228,10 +240,7 @@ class TransitionController {
*/
boolean inCollectingTransition(@NonNull WindowContainer wc) {
if (!isCollecting()) return false;
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (mCollectingTransition.mParticipants.contains(p)) return true;
- }
- return false;
+ return mCollectingTransition.isInTransition(wc);
}
/**
@@ -247,9 +256,7 @@ class TransitionController {
*/
boolean inPlayingTransition(@NonNull WindowContainer wc) {
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (mPlayingTransitions.get(i).mParticipants.contains(p)) return true;
- }
+ if (mPlayingTransitions.get(i).isInTransition(wc)) return true;
}
return false;
}
@@ -469,6 +476,7 @@ class TransitionController {
void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc) {
final Transition transition = mCollectingTransition;
if (transition == null || !transition.mParticipants.contains(wc)) return;
+ transition.collectVisibleChange(wc);
// Collect all visible tasks.
wc.forAllLeafTasks(task -> {
if (task.isVisible()) {
@@ -488,6 +496,16 @@ class TransitionController {
}
}
+ /**
+ * Records that a particular container is changing visibly (ie. something about it is changing
+ * while it remains visible). This only effects windows that are already in the collecting
+ * transition.
+ */
+ void collectVisibleChange(WindowContainer wc) {
+ if (!isCollecting()) return;
+ mCollectingTransition.collectVisibleChange(wc);
+ }
+
/** @see Transition#mStatusBarTransitionDelay */
void setStatusBarTransitionDelay(long delay) {
if (mCollectingTransition == null) return;
@@ -637,11 +655,9 @@ class TransitionController {
}
void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
- final boolean keyguardGoingAway = info.isKeyguardGoingAway();
for (int i = 0; i < mLegacyListeners.size(); ++i) {
// TODO(shell-transitions): handle (un)occlude transition.
- mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
- false /* keyguardOcclude */, 0 /* durationHint */,
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(
SystemClock.uptimeMillis() + statusBarTransitionDelay,
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
}
@@ -656,7 +672,7 @@ class TransitionController {
void dispatchLegacyAppTransitionCancelled() {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
mLegacyListeners.get(i).onAppTransitionCancelledLocked(
- false /* keyguardGoingAway */);
+ false /* keyguardGoingAwayCancelled */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 73658b2dc301..d820ec44ebf6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -343,6 +343,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
BLASTSyncEngine.SyncGroup mSyncGroup = null;
final SurfaceControl.Transaction mSyncTransaction;
@SyncState int mSyncState = SYNC_STATE_NONE;
+ int mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
private final List<WindowContainerListener> mListeners = new ArrayList<>();
@@ -2829,6 +2830,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+ mDisplayContent.mTransitionController.collectVisibleChange(this);
// TODO(b/207070762): request shell transition for activityEmbedding change.
return;
}
@@ -3662,6 +3664,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
boolean onSyncFinishedDrawing() {
if (mSyncState == SYNC_STATE_NONE) return false;
mSyncState = SYNC_STATE_READY;
+ mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
mWmService.mWindowPlacerLocked.requestTraversal();
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "onSyncFinishedDrawing %s", this);
return true;
@@ -3680,6 +3683,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSyncGroup = group;
}
+ @Nullable
+ BLASTSyncEngine.SyncGroup getSyncGroup() {
+ if (mSyncGroup != null) return mSyncGroup;
+ if (mParent != null) return mParent.getSyncGroup();
+ return null;
+ }
+
/**
* Prepares this container for participation in a sync-group. This includes preparing all its
* children.
@@ -3719,6 +3729,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
mSyncState = SYNC_STATE_NONE;
+ mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
mSyncGroup = null;
}
@@ -3821,6 +3832,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// disable this when shell transitions is disabled.
if (mTransitionController.isShellTransitionsEnabled()) {
mSyncState = SYNC_STATE_NONE;
+ mSyncMethodOverride = BLASTSyncEngine.METHOD_UNDEFINED;
}
prepareSync();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a71c3866ba38..11475ac6150b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -220,9 +220,10 @@ public abstract class WindowManagerInternal {
/**
* Called when a pending app transition gets cancelled.
*
- * @param keyguardGoingAway true if keyguard going away transition got cancelled.
+ * @param keyguardGoingAwayCancelled {@code true} if keyguard going away transition was
+ * cancelled.
*/
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {}
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {}
/**
* Called when an app transition is timed out.
@@ -232,9 +233,6 @@ public abstract class WindowManagerInternal {
/**
* Called when an app transition gets started
*
- * @param keyguardGoingAway true if keyguard going away transition is started.
- * @param keyguardOccluding true if keyguard (un)occlude transition is started.
- * @param duration the total duration of the transition
* @param statusBarAnimationStartTime the desired start time for all visual animations in
* the status bar caused by this app transition in uptime millis
* @param statusBarAnimationDuration the duration for all visual animations in the status
@@ -245,8 +243,7 @@ public abstract class WindowManagerInternal {
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
*/
- public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
- boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 22411bb068a0..8025cb296b32 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -92,7 +92,6 @@ import static android.view.WindowManager.fixScale;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
@@ -321,6 +320,7 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
+import com.android.server.wm.utils.WmDisplayCutout;
import dalvik.annotation.optimization.NeverCompile;
@@ -425,32 +425,6 @@ public class WindowManagerService extends IWindowManager.Stub
SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
/**
- * Run Keyguard animation as remote animation in System UI instead of local animation in
- * the server process.
- *
- * 0: Runs all keyguard animation as local animation
- * 1: Only runs keyguard going away animation as remote animation
- * 2: Runs all keyguard animation as remote animation
- */
- private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
- "persist.wm.enable_remote_keyguard_animation";
-
- private static final int sEnableRemoteKeyguardAnimation =
- SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2);
-
- /**
- * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
- */
- public static final boolean sEnableRemoteKeyguardGoingAwayAnimation =
- sEnableRemoteKeyguardAnimation >= 1;
-
- /**
- * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
- */
- public static final boolean sEnableRemoteKeyguardOccludeAnimation =
- sEnableRemoteKeyguardAnimation >= 2;
-
- /**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
* when the display has different orientation.
*/
@@ -1118,7 +1092,7 @@ public class WindowManagerService extends IWindowManager.Stub
= new WindowManagerInternal.AppTransitionListener() {
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
}
@Override
@@ -1873,7 +1847,8 @@ public class WindowManagerService extends IWindowManager.Stub
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
- if (win.isVisibleRequestedOrAdding() && displayContent.updateOrientation()) {
+ if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
+ || win.providesNonDecorInsets()) {
displayContent.sendNewConfiguration();
}
@@ -2583,7 +2558,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int maybeSyncSeqId;
if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE
&& win.mSyncSeqId > lastSyncSeqId) {
- maybeSyncSeqId = win.mSyncSeqId;
+ maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
win.markRedrawForSyncReported();
} else {
maybeSyncSeqId = -1;
@@ -6384,27 +6359,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- /**
- * Used by ActivityManager to determine where to position an app with aspect ratio shorter then
- * the screen is.
- * @see DisplayPolicy#getNavBarPosition()
- */
- @Override
- @WindowManagerPolicy.NavigationBarPosition
- public int getNavBarPosition(int displayId) {
- synchronized (mGlobalLock) {
- // Perform layout if it was scheduled before to make sure that we get correct nav bar
- // position when doing rotations.
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent == null) {
- Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
- + " callers=" + Debug.getCallers(3));
- return NAV_BAR_INVALID;
- }
- return displayContent.getDisplayPolicy().getNavBarPosition();
- }
- }
-
@Override
public void createInputConsumer(IBinder token, String name, int displayId,
InputChannel inputChannel) {
@@ -7242,7 +7196,9 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null) {
final DisplayInfo di = dc.getDisplayInfo();
- dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.displayCutout, outInsets);
+ final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(di.rotation);
+ dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ cutout, outInsets);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 68b1d354272d..f83925507512 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1405,7 +1405,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private BLASTSyncEngine.SyncGroup prepareSyncWithOrganizer(
IWindowContainerTransactionCallback callback) {
final BLASTSyncEngine.SyncGroup s = mService.mWindowManager.mSyncEngine
- .prepareSyncSet(this, "");
+ .prepareSyncSet(this, "", BLASTSyncEngine.METHOD_BLAST);
mTransactionCallbacksByPendingSyncId.put(s.mSyncId, callback);
return s;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d2b9bda7d742..346930311068 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -242,6 +242,7 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -390,7 +391,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
int mSyncSeqId = 0;
- /** The last syncId associated with a prepareSync or 0 when no sync is active. */
+ /** The last syncId associated with a BLAST prepareSync or 0 when no BLAST sync is active. */
int mPrepareSyncSeqId = 0;
/**
@@ -1896,6 +1897,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
}
+ boolean providesNonDecorInsets() {
+ if (mProvidedInsetsSources == null) {
+ return false;
+ }
+ for (int i = mProvidedInsetsSources.size() - 1; i >= 0; i--) {
+ final int type = mProvidedInsetsSources.keyAt(i);
+ if ((InsetsState.toPublicType(type) & WindowInsets.Type.navigationBars()) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
mPolicyVisibility &= ~policyVisibilityFlag;
mWmService.scheduleAnimationLocked();
@@ -2608,14 +2622,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
removeImmediately();
- // Removing a visible window will effect the computed orientation
- // So just update orientation if needed.
+ boolean sentNewConfig = false;
if (wasVisible) {
+ // Removing a visible window will effect the computed orientation
+ // So just update orientation if needed.
final DisplayContent displayContent = getDisplayContent();
if (displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
+ sentNewConfig = true;
}
}
+ if (!sentNewConfig && providesNonDecorInsets()) {
+ getDisplayContent().sendNewConfiguration();
+ }
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
@@ -3892,9 +3911,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
fillClientWindowFramesAndConfiguration(mClientWindowFrames, mLastReportedConfiguration,
true /* useLatestConfig */, false /* relayoutVisible */);
final boolean syncRedraw = shouldSendRedrawForSync();
+ final boolean syncWithBuffers = syncRedraw && shouldSyncWithBuffers();
final boolean reportDraw = syncRedraw || drawPending;
final boolean isDragResizeChanged = isDragResizeChanged();
- final boolean forceRelayout = syncRedraw || isDragResizeChanged;
+ final boolean forceRelayout = syncWithBuffers || isDragResizeChanged;
final DisplayContent displayContent = getDisplayContent();
final boolean alwaysConsumeSystemBars =
displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
@@ -3920,7 +3940,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
try {
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
- mSyncSeqId, resizeMode);
+ syncWithBuffers ? mSyncSeqId : -1, resizeMode);
if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
.getMergedConfiguration().windowConfiguration.getRotation()) {
mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
@@ -5924,7 +5944,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
mSyncSeqId++;
- mPrepareSyncSeqId = mSyncSeqId;
+ if (getSyncMethod() == BLASTSyncEngine.METHOD_BLAST) {
+ mPrepareSyncSeqId = mSyncSeqId;
+ }
requestRedrawForSync();
return true;
}
@@ -5997,6 +6019,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
postDrawTransaction = null;
skipLayout = true;
} else if (syncActive) {
+ // Currently in a Sync that is using BLAST.
if (!syncStillPending) {
onSyncFinishedDrawing();
}
@@ -6005,6 +6028,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Consume the transaction because the sync group will merge it.
postDrawTransaction = null;
}
+ } else if (useBLASTSync()) {
+ // Sync that is not using BLAST
+ onSyncFinishedDrawing();
}
final boolean layoutNeeded =
@@ -6063,6 +6089,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return useBLASTSync();
}
+ int getSyncMethod() {
+ final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
+ if (syncGroup == null) return BLASTSyncEngine.METHOD_NONE;
+ if (mSyncMethodOverride != BLASTSyncEngine.METHOD_UNDEFINED) return mSyncMethodOverride;
+ return syncGroup.mSyncMethod;
+ }
+
+ boolean shouldSyncWithBuffers() {
+ if (!mDrawHandlers.isEmpty()) return true;
+ return getSyncMethod() == BLASTSyncEngine.METHOD_BLAST;
+ }
+
void requestRedrawForSync() {
mRedrawForSyncReported = false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 054181dfa728..0305c35b1828 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -671,6 +671,21 @@ class DevicePolicyData {
if (mFactoryResetReason != null) {
pw.print("mFactoryResetReason="); pw.println(mFactoryResetReason);
}
+ if (mDelegationMap.size() != 0) {
+ pw.println("mDelegationMap=");
+ pw.increaseIndent();
+ for (int i = 0; i < mDelegationMap.size(); i++) {
+ List<String> delegationScopes = mDelegationMap.valueAt(i);
+ pw.println(mDelegationMap.keyAt(i) + "[size=" + delegationScopes.size()
+ + "]");
+ pw.increaseIndent();
+ for (int j = 0; j < delegationScopes.size(); j++) {
+ pw.println(j + ": " + delegationScopes.get(j));
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
pw.decreaseIndent();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4f67f1d3aa28..fbaf1ce25b85 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8144,7 +8144,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+ || isCameraServerUid(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -9666,6 +9667,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
+ private boolean isCameraServerUid(CallerIdentity caller) {
+ return UserHandle.isSameApp(caller.getUid(), Process.CAMERASERVER_UID);
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
UserInfo currentUser = mInjector.getIActivityManager().getCurrentUser();
diff --git a/services/proguard.flags b/services/proguard.flags
index c9303462fb6a..606f360f2cc5 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -104,6 +104,9 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
<methods>;
}
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl {
+ <methods>;
+}
-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
*** usbDeviceRemoved(...);
*** usbDeviceAdded(...);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
index 4a631a1251d5..59cb43f16ef6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -33,6 +33,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.BatteryManagerInternal;
import android.os.RemoteException;
@@ -75,6 +76,8 @@ public class BatteryControllerTest {
private JobSchedulerService mJobSchedulerService;
@Mock
private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private PackageManager mPackageManager;
@Before
public void setUp() {
@@ -100,6 +103,9 @@ public class BatteryControllerTest {
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
mFlexibilityController =
new FlexibilityController(mJobSchedulerService, mock(PrefetchController.class));
mBatteryController = new BatteryController(mJobSchedulerService, mFlexibilityController);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 953a72d5085c..1f85f2c2c46e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -51,6 +51,7 @@ import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -96,6 +97,8 @@ public class ConnectivityControllerTest {
private NetworkPolicyManagerInternal mNetPolicyManagerInternal;
@Mock
private JobSchedulerService mService;
+ @Mock
+ private PackageManager mPackageManager;
private Constants mConstants;
@@ -115,10 +118,6 @@ public class ConnectivityControllerTest {
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
- when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
- mFlexibilityController =
- new FlexibilityController(mService, mock(PrefetchController.class));
-
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -142,6 +141,13 @@ public class ConnectivityControllerTest {
when(mService.getTestableContext()).thenReturn(mContext);
when(mService.getLock()).thenReturn(mService);
when(mService.getConstants()).thenReturn(mConstants);
+ // Instantiate Flexibility Controller
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
+ mFlexibilityController =
+ new FlexibilityController(mService, mock(PrefetchController.class));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 9d039bd09a2f..0eb0a002fe6e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -29,10 +29,12 @@ import static com.android.server.job.controllers.FlexibilityController.FcConfig.
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
+import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -48,6 +50,7 @@ import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -90,6 +93,8 @@ public class FlexibilityControllerTest {
private JobSchedulerService mJobSchedulerService;
@Mock
private PrefetchController mPrefetchController;
+ @Mock
+ private PackageManager mPackageManager;
@Before
public void setup() {
@@ -107,6 +112,9 @@ public class FlexibilityControllerTest {
// Called in FlexibilityController constructor.
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false);
// Used in FlexibilityController.FcConstants.
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.addOnPropertiesChangedListener(
@@ -190,7 +198,7 @@ public class FlexibilityControllerTest {
*/
@Test
public void testDefaultVariableValues() {
- assertEquals(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS,
+ assertEquals(NUM_FLEXIBLE_CONSTRAINTS,
mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length
);
}
@@ -390,7 +398,7 @@ public class FlexibilityControllerTest {
}
@Test
- public void testGetLifeCycleBeginningElapsedLocked_prefetch() {
+ public void testGetLifeCycleBeginningElapsedLocked_Prefetch() {
// prefetch with lifecycle
when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(700L);
JobInfo.Builder jb = createJob(0).setPrefetch(true);
@@ -417,7 +425,7 @@ public class FlexibilityControllerTest {
}
@Test
- public void testGetLifeCycleBeginningElapsedLocked_nonPrefetch() {
+ public void testGetLifeCycleBeginningElapsedLocked_NonPrefetch() {
// delay
long delay = 100;
JobInfo.Builder jb = createJob(0).setMinimumLatency(delay);
@@ -432,7 +440,7 @@ public class FlexibilityControllerTest {
}
@Test
- public void testGetLifeCycleEndElapsedLocked_prefetch() {
+ public void testGetLifeCycleEndElapsedLocked_Prefetch() {
// prefetch no estimate
JobInfo.Builder jb = createJob(0).setPrefetch(true);
JobStatus js = createJobStatus("time", jb);
@@ -444,8 +452,9 @@ public class FlexibilityControllerTest {
when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(1000L);
assertEquals(1000L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
}
+
@Test
- public void testGetLifeCycleEndElapsedLocked_nonPrefetch() {
+ public void testGetLifeCycleEndElapsedLocked_NonPrefetch() {
// deadline
JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
JobStatus js = createJobStatus("time", jb);
@@ -459,6 +468,28 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testGetLifeCycleEndElapsedLocked_Rescheduled() {
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
+ JobStatus js = createJobStatus("time", jb);
+ js = new JobStatus(
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, FROZEN_TIME, FROZEN_TIME);
+
+ assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+
+ js = new JobStatus(
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 3, FROZEN_TIME, FROZEN_TIME);
+
+ assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+
+ js = new JobStatus(
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 10, FROZEN_TIME, FROZEN_TIME);
+ assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+ }
+
+ @Test
public void testWontStopJobFromRunning() {
JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
// Stop satisfied constraints from causing a false positive.
@@ -473,9 +504,9 @@ public class FlexibilityControllerTest {
public void testFlexibilityTracker() {
FlexibilityController.FlexibilityTracker flexTracker =
mFlexibilityController.new
- FlexibilityTracker(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS);
-
- assertEquals(4, flexTracker.size());
+ FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
+ // Plus one for jobs with 0 required constraint.
+ assertEquals(NUM_FLEXIBLE_CONSTRAINTS + 1, flexTracker.size());
JobStatus[] jobs = new JobStatus[4];
JobInfo.Builder jb;
for (int i = 0; i < jobs.length; i++) {
@@ -495,47 +526,53 @@ public class FlexibilityControllerTest {
synchronized (mFlexibilityController.mLock) {
ArrayList<ArraySet<JobStatus>> trackedJobs = flexTracker.getArrayList();
- assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
- assertEquals(3, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(3, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
- assertEquals(0, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
- assertEquals(2, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(1, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(1, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
- assertEquals(0, trackedJobs.get(1).size());
- assertEquals(2, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
- assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(2, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
- assertEquals(2, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.remove(jobs[1]);
- assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(2, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
- assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(1, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
- assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(2, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
- assertEquals(2, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
-
- assertEquals(1, trackedJobs.get(0).size());
- assertEquals(0, trackedJobs.get(1).size());
- assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(2, trackedJobs.get(0).size());
+ assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(1, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
final long nowElapsed = ((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
+ HOUR_IN_MILLIS);
@@ -543,10 +580,11 @@ public class FlexibilityControllerTest {
Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
- assertEquals(0, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(2, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(1, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
}
}
@@ -578,6 +616,15 @@ public class FlexibilityControllerTest {
}
@Test
+ public void testExceptions_RescheduledOnce() {
+ JobInfo.Builder jb = createJob(0);
+ JobStatus js = createJobStatus("time", jb);
+ js = new JobStatus(
+ js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, FROZEN_TIME, FROZEN_TIME);
+ assertFalse(js.hasFlexibilityConstraint());
+ }
+
+ @Test
public void testExceptions_None() {
JobInfo.Builder jb = createJob(0);
JobStatus js = createJobStatus("testExceptions_None", jb);
@@ -818,6 +865,29 @@ public class FlexibilityControllerTest {
}
+ @Test
+ public void testDeviceDisabledFlexibility_Auto() {
+ when(mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
+ mFlexibilityController =
+ new FlexibilityController(mJobSchedulerService, mPrefetchController);
+ assertFalse(mFlexibilityController.mFlexibilityEnabled);
+
+ JobStatus js = createJobStatus("testIsAuto", createJob(0));
+
+ mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+ assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE));
+
+ setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ assertFalse(mFlexibilityController.mFlexibilityEnabled);
+
+ ArrayList<ArraySet<JobStatus>> jobs =
+ mFlexibilityController.mFlexibilityTracker.getArrayList();
+ for (int i = 0; i < jobs.size(); i++) {
+ assertEquals(0, jobs.get(i).size());
+ }
+ }
+
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index d90d8b8bfac0..5cf026e64f67 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -68,7 +68,8 @@ public class ScribeTest {
private MockitoSession mMockingSession;
private Scribe mScribeUnderTest;
private File mTestFileDir;
- private final List<InstalledPackageInfo> mInstalledPackages = new ArrayList<>();
+ private final SparseArrayMap<String, InstalledPackageInfo> mInstalledPackages =
+ new SparseArrayMap<>();
private final List<Analyst.Report> mReports = new ArrayList<>();
@Mock
@@ -455,6 +456,6 @@ public class ScribeTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode()));
pkgInfo.applicationInfo = applicationInfo;
- mInstalledPackages.add(new InstalledPackageInfo(pkgInfo));
+ mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(pkgInfo));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
index 842b23c91e41..4a16874c7acf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -17,13 +17,13 @@
package com.android.server.accessibility;
-import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
import static org.junit.Assert.assertEquals;
@@ -528,7 +528,8 @@ public class AccessibilityInteractionControllerNodeRequestsTest {
// different client that holds different fetch flags for TextView1.
sendNodeRequestToController(nodeId, mMockClientCallback2,
mMockClient2InteractionId,
- FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ FLAG_PREFETCH_SIBLINGS
+ | FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
| FLAG_PREFETCH_ANCESTORS);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 3de006cea15f..81e06649ebc4 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -507,7 +507,7 @@ public class BackupManagerServiceTest {
}
@Test
- public void dump_callerDoesNotHavePermission_ignored() {
+ public void dump_callerDoesNotHaveDumpPermission_ignored() {
when(mContextMock.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP)).thenReturn(
PackageManager.PERMISSION_DENIED);
@@ -518,6 +518,18 @@ public class BackupManagerServiceTest {
verifyNoMoreInteractions(mNonSystemUserBackupManagerService);
}
+ @Test
+ public void dump_callerDoesNotHavePackageUsageStatsPermission_ignored() {
+ when(mContextMock.checkCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+
+ mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
+
+ verifyNoMoreInteractions(mUserBackupManagerService);
+ verifyNoMoreInteractions(mNonSystemUserBackupManagerService);
+ }
+
/**
* Test that {@link BackupManagerService#dump()} dumps system user information before non-system
* user information.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 0e30782eaece..3b66eaba6129 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -80,6 +80,8 @@ public class BiometricStateCallbackTest {
when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true);
when(mUserManager.getAliveUsers()).thenReturn(
List.of(new UserInfo(USER_ID, "name", 0)));
+ when(mBiometricStateListener.asBinder()).thenReturn(mBiometricStateListener);
+
mCallback = new BiometricStateCallback<>(mUserManager);
mCallback.registerBiometricStateListener(mBiometricStateListener);
@@ -110,6 +112,14 @@ public class BiometricStateCallbackTest {
false /* expectCallback */, false /* expectedCallbackValue */);
}
+ @Test
+ public void testBinderDeath() {
+ mCallback.binderDied(mBiometricStateListener.asBinder());
+
+ testEnrollmentCallback(true /* changed */, false /* isNowEnrolled */,
+ false /* expectCallback */, false /* expectedCallbackValue */);
+ }
+
private void testEnrollmentCallback(boolean changed, boolean isNowEnrolled,
boolean expectCallback, boolean expectedCallbackValue) {
EnrollClient<?> client = mock(EnrollClient.class);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 0841bfc1d88c..7ce7ac9a84cf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -17,6 +17,7 @@ package com.android.server.pm;
import static com.android.server.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertArrayEquals;
@@ -101,7 +102,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
@@ -260,6 +260,38 @@ public class PackageParserTest {
}
/**
+ * Extracts the asset file to $mTmpDir/$dirname/$filename.
+ */
+ private File extractFile(String filename, String dirname) throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ File dir = new File(mTmpDir, dirname);
+ dir.mkdir();
+ final File tmpFile = new File(dir, filename);
+ try (InputStream inputStream = context.getAssets().openNonAsset(filename)) {
+ Files.copy(inputStream, tmpFile.toPath(), REPLACE_EXISTING);
+ }
+ return tmpFile;
+ }
+
+ /**
+ * Tests the path of cached ParsedPackage.
+ */
+ @Test
+ public void testCache_SameFileName() throws Exception {
+ // Prepare 2 package files with the same name but different paths
+ TestPackageParser2 parser = new TestPackageParser2(mTmpDir);
+ final File f1 = extractFile(TEST_APP1_APK, "dir1");
+ final File f2 = extractFile(TEST_APP1_APK, "dir2");
+ // Sleep for a while so that the cache file will be newer and valid
+ Thread.sleep(1000);
+ ParsedPackage pr1 = parser.parsePackage(f1, 0, true);
+ ParsedPackage pr2 = parser.parsePackage(f2, 0, true);
+ // Check the path of cached ParsedPackage
+ assertThat(pr1.getPath()).isEqualTo(f1.getAbsolutePath());
+ assertThat(pr2.getPath()).isEqualTo(f2.getAbsolutePath());
+ }
+
+ /**
* Tests AndroidManifest.xml with no android:isolatedSplits attribute.
*/
@Test
@@ -632,8 +664,8 @@ public class PackageParserTest {
}
/**
- * A trivial subclass of package parser that only caches the package name, and throws away
- * all other information.
+ * A subclass of package parser that adds a "cache_" prefix to the package name for the cached
+ * results. This is used by tests to tell if a ParsedPackage is generated from the cache or not.
*/
public static class CachePackageNameParser extends PackageParser2 {
@@ -657,15 +689,10 @@ public class PackageParserTest {
void setCacheDir(@NonNull File cacheDir) {
this.mCacher = new PackageCacher(cacheDir) {
@Override
- public byte[] toCacheEntry(ParsedPackage pkg) {
- return ("cache_" + pkg.getPackageName()).getBytes(StandardCharsets.UTF_8);
- }
-
- @Override
public ParsedPackage fromCacheEntry(byte[] cacheEntry) {
- return ((ParsedPackage) PackageImpl.forTesting(
- new String(cacheEntry, StandardCharsets.UTF_8))
- .hideAsParsed());
+ ParsedPackage parsed = super.fromCacheEntry(cacheEntry);
+ parsed.setPackageName("cache_" + parsed.getPackageName());
+ return parsed;
}
};
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
index 6ee91b89cadc..2332817911f7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
@@ -17,9 +17,11 @@
package com.android.server.pm.parsing
import android.content.pm.ApplicationInfo
+import java.io.File
-class TestPackageParser2 : PackageParser2(null /* separateProcesses */, null /* displayMetrics */,
- null /* cacheDir */, object : PackageParser2.Callback() {
+class TestPackageParser2(var cacheDir: File? = null) : PackageParser2(
+ null /* separateProcesses */, null /* displayMetrics */,
+ cacheDir /* cacheDir */, object : PackageParser2.Callback() {
override fun isChangeEnabled(changeId: Long, appInfo: ApplicationInfo): Boolean {
return true
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 61a7f3853746..5c9348525861 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -28,6 +28,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.Clock;
import org.junit.Before;
import org.junit.Test;
@@ -49,13 +50,14 @@ public class BatteryStatsHistoryTest {
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
private File mHistoryDir;
+ private final Clock mClock = new MockClock();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
mSystemDir = context.getDataDir();
- mHistoryDir = new File(mSystemDir, BatteryStatsHistory.HISTORY_DIR);
+ mHistoryDir = new File(mSystemDir, "battery-history");
String[] files = mHistoryDir.list();
if (files != null) {
for (int i = 0; i < files.length; i++) {
@@ -67,8 +69,8 @@ public class BatteryStatsHistoryTest {
@Test
public void testConstruct() {
- BatteryStatsHistory history =
- new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
+ BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
+ null, mClock);
createActiveFile(history);
verifyFileNumbers(history, Arrays.asList(0));
verifyActiveFile(history, "0.bin");
@@ -76,8 +78,8 @@ public class BatteryStatsHistoryTest {
@Test
public void testStartNextFile() {
- BatteryStatsHistory history =
- new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
+ BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
+ null, mClock);
List<Integer> fileList = new ArrayList<>();
fileList.add(0);
createActiveFile(history);
@@ -114,13 +116,13 @@ public class BatteryStatsHistoryTest {
assertEquals(0, history.getHistoryUsedSize());
// create a new BatteryStatsHistory object, it will pick up existing history files.
- BatteryStatsHistory history2 =
- new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
- // verify construct can pick up all files from file system.
+ BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
+ null, mClock);
+ // verify constructor can pick up all files from file system.
verifyFileNumbers(history2, fileList);
verifyActiveFile(history2, "33.bin");
- history2.resetAllFiles();
+ history2.reset();
createActiveFile(history2);
// verify all existing files are deleted.
for (int i = 2; i < 33; ++i) {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 713e78638b95..570b2ee617f5 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -63,6 +63,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
MockBatteryStatsImpl(Clock clock, File historyDirectory) {
super(clock, historyDirectory);
initTimersAndCounters();
+ setMaxHistoryBuffer(128 * 1024);
setExternalStatsSyncLocked(mExternalStatsSync);
informThatAllExternalStatsAreFlushed();
@@ -104,12 +105,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return mForceOnBattery ? true : super.isOnBattery();
}
- public void forceRecordAllHistory() {
- mHaveBatteryLevel = true;
- mRecordingHistory = true;
- mRecordAllHistory = true;
- }
-
public TimeBase getOnBatteryBackgroundTimeBase(int uid) {
return getUidStatsLocked(uid).mOnBatteryBackgroundTimeBase;
}
@@ -201,12 +196,14 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
@GuardedBy("this")
public MockBatteryStatsImpl setMaxHistoryFiles(int maxHistoryFiles) {
mConstants.MAX_HISTORY_FILES = maxHistoryFiles;
+ mConstants.onChange();
return this;
}
@GuardedBy("this")
public MockBatteryStatsImpl setMaxHistoryBuffer(int maxHistoryBuffer) {
mConstants.MAX_HISTORY_BUFFER = maxHistoryBuffer;
+ mConstants.onChange();
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index ca162efe0f6e..efc240d3f172 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -574,7 +574,7 @@ public class VibrationThreadTest {
}
@Test
- public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() throws Exception {
+ public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
@@ -666,6 +666,47 @@ public class VibrationThreadTest {
}
@Test
+ public void vibrate_singleVibratorComposedWithFallback_replacedInTheMiddleOfComposition() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ fakeVibrator.setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+
+ long vibrationId = 1;
+ VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .compose();
+ Vibration vib = createVibration(vibrationId, CombinedVibration.createParallel(effect));
+ vib.addFallback(VibrationEffect.EFFECT_TICK, fallback);
+ startThreadAndDispatcher(vib);
+ waitForCompletion();
+
+ // Use first duration the vibrator is turned on since we cannot estimate the clicks.
+ verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks).noteVibratorOff(eq(UID));
+ verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+
+ List<VibrationEffectSegment> segments =
+ mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId);
+ assertTrue("Wrong segments: " + segments, segments.size() >= 4);
+ assertTrue(segments.get(0) instanceof PrebakedSegment);
+ assertTrue(segments.get(1) instanceof PrimitiveSegment);
+ for (int i = 2; i < segments.size() - 1; i++) {
+ // One or more step segments as fallback for the EFFECT_TICK.
+ assertTrue(segments.get(i) instanceof StepSegment);
+ }
+ assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment);
+ }
+
+ @Test
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 36bec750e3bc..b8e1612049f0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -72,6 +72,7 @@ import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.Presubmit;
@@ -99,6 +100,7 @@ import org.mockito.junit.MockitoRule;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -391,22 +393,22 @@ public class VibratorManagerServiceTest {
IVibratorStateListener listenerMock = mockVibratorStateListener();
service.registerVibratorStateListener(1, listenerMock);
- vibrate(service, VibrationEffect.createOneShot(40, 100), ALARM_ATTRS);
- // Wait until service knows vibrator is on.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- // Wait until effect ends.
- assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ long oneShotDuration = 20;
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.createOneShot(oneShotDuration, VibrationEffect.DEFAULT_AMPLITUDE),
+ ALARM_ATTRS);
InOrder inOrderVerifier = inOrder(listenerMock);
// First notification done when listener is registered.
inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
- inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
+ // The last notification is after the vibration has completed.
+ inOrderVerifier.verify(listenerMock, timeout(TEST_TIMEOUT_MILLIS)).onVibrating(eq(false));
inOrderVerifier.verifyNoMoreInteractions();
InOrder batteryVerifier = inOrder(mBatteryStatsMock);
batteryVerifier.verify(mBatteryStatsMock)
- .noteVibratorOn(UID, 40 + mVibrationConfig.getRampDownDurationMs());
+ .noteVibratorOn(UID, oneShotDuration + mVibrationConfig.getRampDownDurationMs());
batteryVerifier.verify(mBatteryStatsMock).noteVibratorOff(UID);
}
@@ -577,22 +579,18 @@ public class VibratorManagerServiceTest {
setRingerMode(AudioManager.RINGER_MODE_SILENT);
VibratorManagerService service = createSystemReadyService();
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
- // Wait before checking it never played.
- assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(),
- service, /* timeout= */ 50));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ RINGTONE_ATTRS);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
service = createSystemReadyService();
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(
+ service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
service = createSystemReadyService();
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(
+ service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
assertEquals(
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
@@ -607,27 +605,18 @@ public class VibratorManagerServiceTest {
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
VibratorManagerService service = createSystemReadyService();
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- // The haptic feedback should be ignored in low power, but not the ringtone. The end
- // of the test asserts which actual effects ended up playing.
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
- service, TEST_TIMEOUT_MILLIS));
- // Allow the ringtone to complete, as the other vibrations won't cancel it.
- assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+ HAPTIC_FEEDBACK_ATTRS);
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ RINGTONE_ATTRS);
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
- /* attrs= */ null);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
- service, TEST_TIMEOUT_MILLIS));
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
- NOTIFICATION_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), /* attrs= */ null);
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), NOTIFICATION_ATTRS);
assertEquals(
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
@@ -693,22 +682,17 @@ public class VibratorManagerServiceTest {
Vibrator.VIBRATION_INTENSITY_HIGH);
VibratorManagerService service = createSystemReadyService();
- VibrationAttributes enforceFreshAttrs = new VibrationAttributes.Builder()
+ VibrationAttributes notificationWithFreshAttrs = new VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_NOTIFICATION)
.setFlags(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)
.build();
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), NOTIFICATION_ATTRS);
- // VibrationThread will start this vibration async, so wait before vibrating a second time.
- assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 0,
- service, TEST_TIMEOUT_MILLIS));
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), enforceFreshAttrs);
- // VibrationThread will start this vibration async, so wait before checking.
- assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 1,
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ NOTIFICATION_ATTRS);
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
+ notificationWithFreshAttrs);
assertEquals(
Arrays.asList(
@@ -784,21 +768,22 @@ public class VibratorManagerServiceTest {
vibrate(service, repeatingEffect, new VibrationAttributes.Builder().setUsage(
VibrationAttributes.USAGE_UNKNOWN).build());
- // VibrationThread will start this vibration async, so wait before checking it started.
+ // VibrationThread will start this vibration async, wait until it has started.
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS);
-
- // Wait before checking it never played a second effect.
- assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
- service, /* timeout= */ 50));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
// The time estimate is recorded when the vibration starts, repeating vibrations
// are capped at BATTERY_STATS_REPEATING_VIBRATION_DURATION (=5000).
verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
+ // No segment played is the prebaked CLICK from the second vibration.
+ assertFalse(
+ mVibratorProviders.get(1).getAllEffectSegments().stream()
+ .anyMatch(segment -> segment instanceof PrebakedSegment));
}
@Test
@@ -811,7 +796,7 @@ public class VibratorManagerServiceTest {
new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
vibrate(service, alarmEffect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it started.
+ // VibrationThread will start this vibration async, wait until it has started.
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
@@ -841,14 +826,15 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
-
- // Wait before checking it never played a second effect.
- assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
- service, /* timeout= */ 50));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
+ // The second vibration shouldn't have played any prebaked segment.
+ assertFalse(
+ mVibratorProviders.get(1).getAllEffectSegments().stream()
+ .anyMatch(segment -> segment instanceof PrebakedSegment));
}
@Test
@@ -856,6 +842,7 @@ public class VibratorManagerServiceTest {
throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
VibrationEffect effect = VibrationEffect.createWaveform(
@@ -866,14 +853,16 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, effect, RINGTONE_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ RINGTONE_ATTRS);
// The second vibration should have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
+ // One segment played is the prebaked CLICK from the second vibration.
+ assertEquals(1,
+ mVibratorProviders.get(1).getAllEffectSegments().stream()
+ .filter(PrebakedSegment.class::isInstance)
+ .count());
}
@Test
@@ -892,12 +881,10 @@ public class VibratorManagerServiceTest {
CombinedVibration effect = CombinedVibration.createParallel(
VibrationEffect.createOneShot(10, 10));
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
+ vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
- // VibrationThread will start this vibration async, so wait before checking it never played.
- assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, /* timeout= */ 50));
+ verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
+ assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty());
}
@Test
@@ -992,9 +979,7 @@ public class VibratorManagerServiceTest {
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose())
.combine();
- vibrate(service, effect, ALARM_ATTRS);
- assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
- TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
verify(mNativeWrapperMock).triggerSynced(anyLong());
@@ -1016,9 +1001,7 @@ public class VibratorManagerServiceTest {
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
.combine();
- vibrate(service, effect, ALARM_ATTRS);
- assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
- TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
verify(mNativeWrapperMock, never()).prepareSynced(any());
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
@@ -1036,9 +1019,7 @@ public class VibratorManagerServiceTest {
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
.combine();
- vibrate(service, effect, ALARM_ATTRS);
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
@@ -1057,9 +1038,7 @@ public class VibratorManagerServiceTest {
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
.combine();
- vibrate(service, effect, ALARM_ATTRS);
- assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
- service, TEST_TIMEOUT_MILLIS));
+ vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
verify(mNativeWrapperMock).triggerSynced(anyLong());
@@ -1096,28 +1075,21 @@ public class VibratorManagerServiceTest {
VibrationEffect.Composition.PRIMITIVE_TICK);
VibratorManagerService service = createSystemReadyService();
- vibrate(service, VibrationEffect.startComposition()
+ vibrateAndWaitUntilFinished(service, VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.compose(), HAPTIC_FEEDBACK_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
- service, TEST_TIMEOUT_MILLIS));
- vibrate(service, CombinedVibration.startSequential()
+ vibrateAndWaitUntilFinished(service, CombinedVibration.startSequential()
.addNext(1, VibrationEffect.createOneShot(100, 125))
.combine(), NOTIFICATION_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
- service, TEST_TIMEOUT_MILLIS));
- vibrate(service, VibrationEffect.startComposition()
+ vibrateAndWaitUntilFinished(service, VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.compose(), ALARM_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
- service, TEST_TIMEOUT_MILLIS));
// Ring vibrations have intensity OFF and are not played.
- vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
- assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3,
- service, /* timeout= */ 50));
+ vibrateAndWaitUntilFinished(service, VibrationEffect.createOneShot(100, 125),
+ RINGTONE_ATTRS);
// Only 3 effects played successfully.
assertEquals(3, fakeVibrator.getAllEffectSegments().size());
@@ -1145,6 +1117,7 @@ public class VibratorManagerServiceTest {
.combine(),
HAPTIC_FEEDBACK_ATTRS);
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
@@ -1159,14 +1132,50 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
service.updateServiceState();
+
// Vibration is not stopped nearly after updating service.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
}
@Test
+ public void vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined()
+ throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
+
+ VibratorManagerService service = createSystemReadyService();
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose(),
+ ALARM_ATTRS);
+
+ List<VibrationEffectSegment> segments = mVibratorProviders.get(1).getAllEffectSegments();
+ // At least one step segment played as fallback for unusupported vibration effect
+ assertTrue(segments.size() > 2);
+ // 0: Supported effect played
+ assertTrue(segments.get(0) instanceof PrebakedSegment);
+ // 1: No segment for unsupported primitive
+ // 2: One or more intermediate step segments as fallback for unsupported effect
+ for (int i = 1; i < segments.size() - 1; i++) {
+ assertTrue(segments.get(i) instanceof StepSegment);
+ }
+ // 3: Supported primitive played
+ assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment);
+ }
+
+ @Test
public void cancelVibrate_withoutUsageFilter_stopsVibrating() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1175,9 +1184,13 @@ public class VibratorManagerServiceTest {
assertFalse(service.isVibrating(1));
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
+
+ // Alarm cancelled on filter match all.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
@@ -1187,6 +1200,8 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
// Vibration is not cancelled with a different usage.
@@ -1216,6 +1231,8 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
// Do not cancel UNKNOWN vibration when filter is being applied for other usages.
@@ -1232,6 +1249,8 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
// Cancel UNKNOWN vibration when all vibrations are being cancelled.
@@ -1312,6 +1331,8 @@ public class VibratorManagerServiceTest {
VibrationEffect effect = VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100);
vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d62ac99e530b..598a22bbde39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -93,7 +93,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.os.Parcel;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -2448,35 +2447,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testGetNotificationChannelGroup() throws Exception {
- NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
- NotificationChannel base =
- new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
- base.setGroup("not");
- NotificationChannel convo =
- new NotificationChannel("convo", "belongs to notDeleted", IMPORTANCE_DEFAULT);
- convo.setGroup("not");
- convo.setConversationId("not deleted", "banana");
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, base, true, false);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, convo, true, false);
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
-
- NotificationChannelGroup g
- = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
- Parcel parcel = Parcel.obtain();
- g.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
-
- NotificationChannelGroup g2
- = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
- Parcel parcel2 = Parcel.obtain();
- g2.writeToParcel(parcel2, 0);
- parcel2.setDataPosition(0);
- }
-
- @Test
public void testOnUserRemoved() throws Exception {
int[] user0Uids = {98, 235, 16, 3782};
int[] user1Uids = new int[user0Uids.length];
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 15d1a3c48ccd..44494831eb68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -158,6 +158,7 @@ import androidx.test.filters.MediumTest;
import com.android.internal.R;
import com.android.server.wm.ActivityRecord.State;
+import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Assert;
import org.junit.Before;
@@ -551,7 +552,8 @@ public class ActivityRecordTests extends WindowTestsBase {
final Rect insets = new Rect();
final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
@@ -592,7 +594,8 @@ public class ActivityRecordTests extends WindowTestsBase {
final Rect insets = new Rect();
final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 2158cafbb64a..aa5a74e962e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -25,10 +25,13 @@ import static org.hamcrest.Matchers.equalTo;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.utils.WmDisplayCutout;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
@@ -46,7 +49,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void portrait() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_0, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -55,7 +59,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void portrait_withCutout() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_0, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
@@ -64,7 +69,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void landscape() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_90, false /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -79,7 +85,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void landscape_withCutout() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_90, true /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -94,7 +101,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void seascape() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_270, false /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
@@ -109,7 +117,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void seascape_withCutout() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_270, true /* withCutout */);
if (mDisplayPolicy.navigationBarCanMove()) {
verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
@@ -124,7 +133,8 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void upsideDown() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_180, false /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -133,28 +143,34 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Test
public void upsideDown_withCutout() {
- final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
+ final Pair<DisplayInfo, WmDisplayCutout> di =
+ displayInfoForRotation(ROTATION_180, true /* withCutout */);
verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
verifyConsistency(di);
}
- private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) {
- mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect(
- left, top, right, bottom)));
+ private void verifyStableInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+ int right, int bottom) {
+ mErrorCollector.checkThat("stableInsets", getStableInsetsLw(diPair.first, diPair.second),
+ equalTo(new Rect(left, top, right, bottom)));
}
- private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) {
- mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect(
+ private void verifyNonDecorInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+ int right, int bottom) {
+ mErrorCollector.checkThat("nonDecorInsets",
+ getNonDecorInsetsLw(diPair.first, diPair.second), equalTo(new Rect(
left, top, right, bottom)));
}
- private void verifyConsistency(DisplayInfo di) {
- verifyConsistency("configDisplay", di, getStableInsetsLw(di),
- getConfigDisplayWidth(di), getConfigDisplayHeight(di));
- verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di),
- getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di));
+ private void verifyConsistency(Pair<DisplayInfo, WmDisplayCutout> diPair) {
+ final DisplayInfo di = diPair.first;
+ final WmDisplayCutout cutout = diPair.second;
+ verifyConsistency("configDisplay", di, getStableInsetsLw(di, cutout),
+ getConfigDisplayWidth(di, cutout), getConfigDisplayHeight(di, cutout));
+ verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di, cutout),
+ getNonDecorDisplayWidth(di, cutout), getNonDecorDisplayHeight(di, cutout));
}
private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width,
@@ -165,39 +181,42 @@ public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
equalTo(di.logicalHeight - insets.top - insets.bottom));
}
- private Rect getStableInsetsLw(DisplayInfo di) {
+ private Rect getStableInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
Rect result = new Rect();
- mDisplayPolicy.getStableInsetsLw(di.rotation, di.displayCutout, result);
+ mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ cutout, result);
return result;
}
- private Rect getNonDecorInsetsLw(DisplayInfo di) {
+ private Rect getNonDecorInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
Rect result = new Rect();
- mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.displayCutout, result);
+ mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ cutout, result);
return result;
}
- private int getNonDecorDisplayWidth(DisplayInfo di) {
- return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ private int getNonDecorDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
+ return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
+ di.rotation, cutout).width();
}
- private int getNonDecorDisplayHeight(DisplayInfo di) {
- return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalHeight, di.rotation,
- di.displayCutout);
+ private int getNonDecorDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
+ return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
+ di.rotation, cutout).height();
}
- private int getConfigDisplayWidth(DisplayInfo di) {
- return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ private int getConfigDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
+ return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, cutout).x;
}
- private int getConfigDisplayHeight(DisplayInfo di) {
- return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ private int getConfigDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
+ return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+ di.rotation, cutout).y;
}
- private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
- return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false).first;
+ private static Pair<DisplayInfo, WmDisplayCutout> displayInfoForRotation(int rotation,
+ boolean withDisplayCutout) {
+ return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 8546763aebec..88e58eab58aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -281,7 +281,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
// Simulate the app transition finishing
- mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
+ mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0);
verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 420ea8e63562..d3aa073c84d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -16,13 +16,18 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.BLASTSyncEngine.METHOD_BLAST;
+import static com.android.server.wm.BLASTSyncEngine.METHOD_NONE;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -67,7 +72,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
// Make sure a traversal is requested
verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
@@ -95,7 +100,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
// Make sure traversals requested (one for add and another for setReady)
@@ -119,7 +124,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
// Make sure traversals requested (one for add and another for setReady)
@@ -147,7 +152,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
bse.setReady(id);
bse.onSurfacePlacement();
@@ -180,7 +185,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
bse.setReady(id);
bse.onSurfacePlacement();
@@ -211,7 +216,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
bse.setReady(id);
bse.onSurfacePlacement();
@@ -243,7 +248,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
bse.setReady(id);
bse.onSurfacePlacement();
@@ -278,7 +283,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
bse.setReady(id);
bse.onSurfacePlacement();
@@ -317,7 +322,7 @@ public class SyncEngineTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener listener = mock(
BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(listener);
+ int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, parentWC);
final BLASTSyncEngine.SyncGroup syncGroup = parentWC.mSyncGroup;
bse.setReady(id);
@@ -350,6 +355,33 @@ public class SyncEngineTests extends WindowTestsBase {
assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
}
+ @Test
+ public void testNonBlastMethod() {
+ mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
+
+ final BLASTSyncEngine bse = createTestBLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener listener = mock(
+ BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = startSyncSet(bse, listener, METHOD_NONE);
+ bse.addToSyncSet(id, mAppWindow.mToken);
+ mAppWindow.prepareSync();
+ assertFalse(mAppWindow.shouldSyncWithBuffers());
+
+ mAppWindow.removeImmediately();
+ }
+
+ static int startSyncSet(BLASTSyncEngine engine,
+ BLASTSyncEngine.TransactionReadyListener listener) {
+ return startSyncSet(engine, listener, METHOD_BLAST);
+ }
+
+ static int startSyncSet(BLASTSyncEngine engine,
+ BLASTSyncEngine.TransactionReadyListener listener, int method) {
+ return engine.startSyncSet(listener, BLAST_TIMEOUT_DURATION, "", method);
+ }
+
static class TestWindowContainer extends WindowContainer {
final boolean mWaiter;
boolean mVisibleRequested = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 1e64e469fe7f..f5304d00faab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,6 +18,13 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -26,12 +33,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -43,6 +47,7 @@ import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
+import android.view.WindowInsets;
import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
@@ -204,7 +209,26 @@ class TestDisplayContent extends DisplayContent {
doReturn(true).when(newDisplay).supportsSystemDecorations();
doReturn(true).when(displayPolicy).hasNavigationBar();
doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt());
- doReturn(20).when(displayPolicy).getNavigationBarHeight(anyInt());
+ doReturn(Insets.of(0, 0, 0, 20)).when(displayPolicy).getInsets(any(),
+ eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()));
+ doReturn(Insets.of(0, 20, 0, 20)).when(displayPolicy).getInsets(any(),
+ eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.statusBars()));
+ final int[] nonDecorTypes = new int[]{
+ ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT,
+ ITYPE_BOTTOM_DISPLAY_CUTOUT, ITYPE_LEFT_DISPLAY_CUTOUT, ITYPE_NAVIGATION_BAR
+ };
+ doReturn(Insets.of(0, 0, 0, 20)).when(displayPolicy).getInsetsWithInternalTypes(
+ any(),
+ eq(nonDecorTypes));
+ final int[] stableTypes = new int[]{
+ ITYPE_TOP_DISPLAY_CUTOUT, ITYPE_RIGHT_DISPLAY_CUTOUT,
+ ITYPE_BOTTOM_DISPLAY_CUTOUT, ITYPE_LEFT_DISPLAY_CUTOUT,
+ ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR
+ };
+ doReturn(Insets.of(0, 20, 0, 20)).when(displayPolicy).getInsetsWithInternalTypes(
+ any(),
+ eq(stableTypes));
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d2cb7ba5d311..13da1543cfb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -310,11 +310,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ public void startKeyguardExitAnimation(long startTime) {
}
@Override
- public int applyKeyguardOcclusionChange(boolean keyguardOccludingStarted) {
+ public int applyKeyguardOcclusionChange(boolean notify) {
return 0;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 85ac7bd96854..55ca5fa65fce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -57,6 +57,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -73,6 +74,7 @@ import android.window.RemoteTransition;
import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -1117,6 +1119,36 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(targets.contains(activity));
}
+ @Test
+ public void testTransitionVisibleChange() {
+ registerTestTransitionPlayer();
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
+ app.mTransitionController, mWm.mSyncEngine);
+ app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
+ final ArrayList<WindowContainer> freezeCalls = new ArrayList<>();
+ transition.setContainerFreezer(new Transition.IContainerFreezer() {
+ @Override
+ public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
+ freezeCalls.add(wc);
+ return true;
+ }
+
+ @Override
+ public void cleanUp(SurfaceControl.Transaction t) {
+ }
+ });
+ final Task task = app.getTask();
+ transition.collect(task);
+ final Rect bounds = new Rect(task.getBounds());
+ Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
+ bounds.inset(10, 10);
+ c.windowConfiguration.setBounds(bounds);
+ task.onRequestedOverrideConfigurationChanged(c);
+ assertTrue(freezeCalls.contains(task));
+ transition.abort();
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index a62625cea46c..3b64c512ca8f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,8 +42,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.BLASTSyncEngine.METHOD_BLAST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
+import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import static com.google.common.truth.Truth.assertThat;
@@ -997,7 +999,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
BLASTSyncEngine.TransactionReadyListener transactionListener =
mock(BLASTSyncEngine.TransactionReadyListener.class);
- int id = bse.startSyncSet(transactionListener);
+ int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "", METHOD_BLAST);
bse.addToSyncSet(id, task);
bse.setReady(id);
bse.onSurfacePlacement();
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 564d3ca2a1c9..6785979e2065 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -331,6 +331,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
mNavBarWindow.mAttrs.setFitInsetsTypes(0);
+ mNavBarWindow.mAttrs.layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mNavBarWindow.mAttrs.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
mNavBarWindow.mAttrs.paramsForRotation[rot] =
getNavBarLayoutParamsForRotation(rot);
@@ -381,6 +385,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
lp.height = height;
lp.gravity = gravity;
lp.setFitInsetsTypes(0);
+ lp.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
return lp;
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 44265511ee50..f1fc503c8556 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -169,6 +169,12 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assertTrue(getDevice().pushFile(apex, "/" + partition + "/apex/" + fileName));
}
+ private void installTestApex(String fileName, String... extraArgs) throws Exception {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ final File apex = buildHelper.getTestFile(fileName);
+ getDevice().installPackage(apex, false, extraArgs);
+ }
+
private void pushTestVendorApexAllowList(String installerPackageName) throws Exception {
if (!getDevice().isAdbRoot()) {
getDevice().enableAdbRoot();
@@ -551,7 +557,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
pushTestApex(REBOOTLESS_V1, "vendor");
getDevice().reboot();
runPhase("testVendorApex_VerifyFactory");
- installPackage(REBOOTLESS_V2, "--staged");
+ installTestApex(REBOOTLESS_V2, "--staged");
getDevice().reboot();
runPhase("testVendorApex_VerifyData");
}
@@ -565,7 +571,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
pushTestApex(REBOOTLESS_V1, "vendor");
getDevice().reboot();
runPhase("testVendorApex_VerifyFactory");
- installPackage(REBOOTLESS_V2, "--force-non-staged");
+ installTestApex(REBOOTLESS_V2, "--force-non-staged");
runPhase("testVendorApex_VerifyData");
}
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index ebe9b5706bf8..edd6dd3468ef 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -30,6 +30,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import java.util.Map;
/**
@@ -45,6 +46,9 @@ public class TestableLooper {
* catch crashes.
*/
public static final boolean HOLD_MAIN_THREAD = false;
+ private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
+ private static final Field MESSAGE_NEXT_FIELD;
+ private static final Field MESSAGE_WHEN_FIELD;
private Looper mLooper;
private MessageQueue mQueue;
@@ -54,6 +58,19 @@ public class TestableLooper {
private Runnable mEmptyMessage;
private TestLooperManager mQueueWrapper;
+ static {
+ try {
+ MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Failed to initialize TestableLooper", e);
+ }
+ }
+
public TestableLooper(Looper l) throws Exception {
this(acquireLooperManager(l), l);
}
@@ -119,6 +136,33 @@ public class TestableLooper {
while (processQueuedMessages() != 0) ;
}
+ public void moveTimeForward(long milliSeconds) {
+ try {
+ Message msg = getMessageLinkedList();
+ while (msg != null) {
+ long updatedWhen = msg.getWhen() - milliSeconds;
+ if (updatedWhen < 0) {
+ updatedWhen = 0;
+ }
+ MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TestableLooper: set - Message.when", e);
+ }
+ }
+
+ private Message getMessageLinkedList() {
+ try {
+ MessageQueue queue = mLooper.getQueue();
+ return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(
+ "Access failed in TestableLooper: get - MessageQueue.mMessages",
+ e);
+ }
+ }
+
private int processQueuedMessages() {
int count = 0;
mEmptyMessage = () -> { };
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 25f6a48871d3..0f491b86626c 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -19,15 +19,19 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import android.os.Handler;
import android.os.Looper;
@@ -162,7 +166,7 @@ public class TestableLooperTest {
@Test
public void testCorrectLooperExecution() throws Exception {
- boolean[] hasRun = new boolean[] { false };
+ boolean[] hasRun = new boolean[]{false};
Runnable r = () -> {
assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper());
hasRun[0] = true;
@@ -177,4 +181,63 @@ public class TestableLooperTest {
testableLooper.destroy();
}
}
+
+ @Test
+ public void testDelayedDispatchNoTimeMove() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ }
+
+ @Test
+ public void testDelayedMessageDoesntSend() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+ final Message messageC = handler.obtainMessage(3);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+ handler.sendMessageDelayed(messageC, 500);
+
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ verify(handler, never()).dispatchMessage(messageC);
+ }
+
+ @Test
+ public void testMessageSendsAfterDelay() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+ final Message messageC = handler.obtainMessage(3);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+ handler.sendMessageDelayed(messageC, 500);
+
+ mTestableLooper.moveTimeForward(500);
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ inOrder.verify(handler).dispatchMessage(messageC);
+ }
+
}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 39ac24b29f35..b3165d320e9b 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1981,10 +1981,11 @@ class Action : public ManifestExtractor::Element {
if (ElementCast<Activity>(parent_stack[1])) {
// Detects the presence of a particular type of activity.
Activity* activity = ElementCast<Activity>(parent_stack[1]);
- auto map = std::map<std::string, std::string>({
- { "android.intent.action.MAIN" , "main" },
- { "android.intent.action.VIDEO_CAMERA" , "camera" },
- { "android.intent.action.STILL_IMAGE_CAMERA_SECURE" , "camera-secure" },
+ static const auto map = std::map<std::string, std::string>({
+ {"android.intent.action.MAIN", "main"},
+ {"android.media.action.VIDEO_CAMERA", "camera"},
+ {"android.media.action.STILL_IMAGE_CAMERA", "camera"},
+ {"android.media.action.STILL_IMAGE_CAMERA_SECURE", "camera-secure"},
});
auto entry = map.find(action);
@@ -2735,10 +2736,9 @@ bool ManifestExtractor::Extract(android::IDiagnostics* diag) {
auto it = apk_->GetFileCollection()->Iterator();
while (it->HasNext()) {
auto file_path = it->Next()->GetSource().path;
- size_t pos = file_path.find("lib/");
- if (pos != std::string::npos) {
- file_path = file_path.substr(pos + 4);
- pos = file_path.find('/');
+ if (file_path.starts_with("lib/")) {
+ file_path = file_path.substr(4);
+ size_t pos = file_path.find('/');
if (pos != std::string::npos) {
file_path = file_path.substr(0, pos);
}
diff --git a/tools/aapt2/integration-tests/DumpTest/components.apk b/tools/aapt2/integration-tests/DumpTest/components.apk
index deb55ea6fda3..a34ec83f3dae 100644
--- a/tools/aapt2/integration-tests/DumpTest/components.apk
+++ b/tools/aapt2/integration-tests/DumpTest/components.apk
Binary files differ
diff --git a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
index bd7673616ddc..8e733a5db034 100644
--- a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
+++ b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
@@ -1677,7 +1677,7 @@ xml_files {
attribute {
namespace_uri: "http://schemas.android.com/apk/res/android"
name: "name"
- value: "android.intent.action.VIDEO_CAMERA"
+ value: "android.media.action.VIDEO_CAMERA"
resource_id: 16842755
}
}
@@ -1691,7 +1691,7 @@ xml_files {
attribute {
namespace_uri: "http://schemas.android.com/apk/res/android"
name: "name"
- value: "android.intent.action.STILL_IMAGE_CAMERA_SECURE"
+ value: "android.media.action.STILL_IMAGE_CAMERA_SECURE"
resource_id: 16842755
}
}
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
index 7c0451900e32..2c526a1e239e 100644
--- a/tools/lint/OWNERS
+++ b/tools/lint/OWNERS
@@ -2,4 +2,5 @@ brufino@google.com
jsharkey@google.com
per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
+per-file *RegisterReceiverFlagDetector* = jacobhobbie@google.com