summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp23
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java4
-rw-r--r--apct-tests/perftests/inputmethod/OWNERS1
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java253
-rw-r--r--api/StubLibraries.bp8
-rw-r--r--core/api/current.txt27
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/api/test-current.txt14
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java227
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl4
-rw-r--r--core/java/android/animation/AnimatorSet.java2
-rw-r--r--core/java/android/app/Activity.java10
-rw-r--r--core/java/android/app/ApplicationPackageManager.java21
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl9
-rw-r--r--core/java/android/app/IUiAutomationConnection.aidl6
-rw-r--r--core/java/android/app/NotificationManager.java6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java6
-rw-r--r--core/java/android/app/TaskInfo.java30
-rw-r--r--core/java/android/app/UiAutomation.java52
-rw-r--r--core/java/android/app/UiAutomationConnection.java37
-rw-r--r--core/java/android/app/WallpaperInfo.java6
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java51
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java10
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/content/BroadcastReceiver.java2
-rw-r--r--core/java/android/content/Context.java6
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl10
-rw-r--r--core/java/android/content/pm/PackageInstaller.java4
-rw-r--r--core/java/android/content/pm/PackageManager.java98
-rw-r--r--core/java/android/content/pm/UserInfo.java4
-rw-r--r--core/java/android/content/pm/dex/DexMetadataHelper.java82
-rw-r--r--core/java/android/content/res/ApkAssets.java4
-rw-r--r--core/java/android/content/res/AssetManager.java4
-rw-r--r--core/java/android/content/res/Element.java405
-rw-r--r--core/java/android/content/res/TagCounter.java73
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java28
-rw-r--r--core/java/android/content/res/TypedArray.java12
-rw-r--r--core/java/android/content/res/Validator.java116
-rw-r--r--core/java/android/content/res/XmlBlock.java22
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java114
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java13
-rw-r--r--core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl6
-rw-r--r--core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl6
-rw-r--r--core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl6
-rw-r--r--core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl6
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java43
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java51
-rw-r--r--core/java/android/hardware/display/DisplayManager.java18
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java80
-rw-r--r--core/java/android/inputmethodservice/OWNERS3
-rw-r--r--core/java/android/net/OWNERS1
-rw-r--r--core/java/android/nfc/NfcAdapter.java25
-rw-r--r--core/java/android/os/BatteryStats.java77
-rw-r--r--core/java/android/os/GraphicsEnvironment.java144
-rw-r--r--core/java/android/os/TEST_MAPPING16
-rw-r--r--core/java/android/os/UserManager.java35
-rw-r--r--core/java/android/os/vibrator/persistence/VibrationXmlParser.java28
-rw-r--r--core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java13
-rw-r--r--core/java/android/provider/Settings.java20
-rw-r--r--core/java/android/service/autofill/augmented/OWNERS8
-rw-r--r--core/java/android/service/dreams/DreamService.java5
-rw-r--r--core/java/android/service/dreams/IDreamManager.aidl2
-rw-r--r--core/java/android/service/wallpaper/IWallpaperService.aidl4
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java22
-rw-r--r--core/java/android/text/MeasuredParagraph.java166
-rw-r--r--core/java/android/text/PrecomputedText.java16
-rw-r--r--core/java/android/text/StaticLayout.java53
-rw-r--r--core/java/android/text/format/DateUtils.java2
-rw-r--r--core/java/android/text/style/LineBreakConfigSpan.java63
-rw-r--r--core/java/android/util/FeatureFlagUtils.java13
-rw-r--r--core/java/android/util/LongSparseArray.java33
-rw-r--r--core/java/android/util/TimeSparseArray.java99
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java35
-rw-r--r--core/java/android/view/IWindowManager.aidl5
-rw-r--r--core/java/android/view/InsetsController.java3
-rw-r--r--core/java/android/view/InsetsSource.java9
-rw-r--r--core/java/android/view/InsetsState.java27
-rw-r--r--core/java/android/view/RoundScrollbarRenderer.java97
-rw-r--r--core/java/android/view/View.java165
-rw-r--r--core/java/android/view/ViewRootImpl.java18
-rw-r--r--core/java/android/view/WindowInsets.java51
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java106
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java1
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl3
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl5
-rw-r--r--core/java/android/view/animation/AnimationUtils.java13
-rw-r--r--core/java/android/view/autofill/OWNERS3
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java4
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java2
-rw-r--r--core/java/android/view/textclassifier/OWNERS7
-rw-r--r--core/java/android/view/translation/OWNERS6
-rw-r--r--core/java/android/widget/TextView.java48
-rw-r--r--core/java/android/widget/inline/OWNERS4
-rw-r--r--core/java/android/window/WindowMetricsController.java10
-rw-r--r--core/java/android/window/WindowProviderService.java15
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java56
-rw-r--r--core/java/com/android/internal/content/PackageMonitor.java103
-rw-r--r--core/java/com/android/internal/infra/ServiceConnector.java4
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java9
-rw-r--r--core/java/com/android/internal/os/CpuScalingPolicies.java98
-rw-r--r--core/java/com/android/internal/os/CpuScalingPolicyReader.java144
-rw-r--r--core/java/com/android/internal/os/KernelCpuUidTimeReader.java73
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java241
-rw-r--r--core/java/com/android/internal/policy/DecorView.java43
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java13
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_hardware_Camera.cpp23
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp17
-rw-r--r--core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml2
-rw-r--r--core/res/res/drawable-watch/global_actions_item_red_background_shape.xml2
-rw-r--r--core/res/res/layout-watch/global_actions_item.xml2
-rw-r--r--core/res/res/values/attrs.xml12
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/xml/sms_short_codes.xml6
-rw-r--r--core/tests/coretests/res/xml/power_profile_test.xml42
-rw-r--r--core/tests/coretests/res/xml/power_profile_test_cpu_legacy.xml116
-rw-r--r--core/tests/coretests/res/xml/power_profile_test_power_brackets.xml22
-rw-r--r--core/tests/coretests/src/android/app/OWNERS3
-rw-r--r--core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java15
-rw-r--r--core/tests/coretests/src/android/text/StaticLayoutTest.java21
-rw-r--r--core/tests/coretests/src/android/util/LongSparseArrayTest.java69
-rw-r--r--core/tests/coretests/src/android/util/TimeSparseArrayTest.java103
-rw-r--r--core/tests/coretests/src/android/view/WindowInsetsTest.java6
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/content/OWNERS4
-rw-r--r--core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java350
-rw-r--r--core/tests/coretests/src/com/android/internal/inputmethod/OWNERS1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java84
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java62
-rw-r--r--core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java89
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java2
-rw-r--r--data/keyboards/Vendor_0957_Product_0001.kl2
-rw-r--r--graphics/java/android/graphics/Bitmap.java7
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java136
-rw-r--r--graphics/java/android/graphics/fonts/FontFileUtil.java71
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java2
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java238
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java6
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java70
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt)23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt276
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt70
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt67
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt80
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt66
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt65
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt70
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt72
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt83
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt70
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt73
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt422
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt151
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt70
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt69
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt69
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt72
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt68
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt2
-rw-r--r--libs/hwui/jni/FontFamily.cpp3
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp7
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp46
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp20
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h36
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp11
-rw-r--r--media/java/android/media/AudioSystem.java5
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java62
-rw-r--r--media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl13
-rw-r--r--media/java/android/media/projection/MediaProjectionManager.java24
-rw-r--r--media/java/android/media/session/MediaSession.java28
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java8
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java38
-rw-r--r--packages/CarrierDefaultApp/assets/slice_purchase_test.html2
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java14
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java11
-rw-r--r--packages/SettingsLib/res/layout/dialog_with_icon.xml4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java125
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java94
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java64
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java35
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java23
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java10
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SoundPicker/Android.bp4
-rw-r--r--packages/SoundPicker/AndroidManifest.xml3
-rw-r--r--packages/SoundPicker/res/layout/activity_ringtone_picker.xml7
-rw-r--r--packages/SoundPicker/res/layout/add_new_sound_item.xml18
-rw-r--r--packages/SoundPicker/res/layout/fragment_sound_picker.xml23
-rw-r--r--packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml31
-rw-r--r--packages/SoundPicker/res/layout/fragment_vibration_picker.xml32
-rw-r--r--packages/SoundPicker/res/layout/radio_with_work_badge.xml10
-rw-r--r--packages/SoundPicker/res/values/strings.xml4
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java117
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtoneAdapter.java268
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java600
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java158
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java332
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java173
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java29
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java70
-rw-r--r--packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java69
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt17
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt60
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt59
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt8
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt1
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt19
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt4
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt2
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt1
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt2
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt (renamed from packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt)2
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt (renamed from packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt)8
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt224
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt42
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java10
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt4
-rw-r--r--packages/SystemUI/proguard_common.flags7
-rw-r--r--packages/SystemUI/res-keyguard/color/shade_disabled.xml (renamed from core/res/res/values-watch/colors.xml)15
-rw-r--r--packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml6
-rw-r--r--packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml25
-rw-r--r--packages/SystemUI/res/drawable/brightness_mirror_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_drawable.xml2
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml4
-rw-r--r--packages/SystemUI/res/drawable/fgs_dot.xml2
-rw-r--r--packages/SystemUI/res/drawable/list_item_background.xml21
-rw-r--r--packages/SystemUI/res/drawable/qs_customizer_background_primary.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_customizer_toolbar.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_circle.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_footer_actions_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_security_footer_background.xml2
-rw-r--r--packages/SystemUI/res/layout/alert_dialog_title_systemui.xml2
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml5
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml3
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item_advanced.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog.xml9
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml8
-rw-r--r--packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml3
-rw-r--r--packages/SystemUI/res/values/attrs.xml21
-rw-r--r--packages/SystemUI/res/values/styles.xml73
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt171
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt152
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt162
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt327
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt195
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt395
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt173
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt168
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt94
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt505
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt212
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt317
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt47
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt28
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java199
-rw-r--r--services/accessibility/TEST_MAPPING16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java30
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java35
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java33
-rw-r--r--services/autofill/OWNERS19
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java16
-rw-r--r--services/core/java/com/android/server/am/PendingIntentController.java15
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java14
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java15
-rw-r--r--services/core/java/com/android/server/am/StackTracesDumpHelper.java33
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java2
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java1
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java92
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java7
-rw-r--r--services/core/java/com/android/server/display/BrightnessRangeController.java23
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java20
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java25
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java3
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/display/NormalBrightnessModeController.java9
-rw-r--r--services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java168
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java305
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java34
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java62
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java31
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java21
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java29
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java15
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java93
-rw-r--r--services/core/java/com/android/server/hdmi/PowerManagerWrapper.java52
-rw-r--r--services/core/java/com/android/server/hdmi/WakeLockWrapper.java52
-rw-r--r--services/core/java/com/android/server/input/InputSettingsObserver.java24
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java110
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java91
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java89
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java33
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java25
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java11
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java54
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java15
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java99
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java180
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java7
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/Settings.java18
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/pm/StorageEventHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java14
-rw-r--r--services/core/java/com/android/server/pm/UserTypeDetails.java8
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java35
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java26
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateImpl.java12
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserState.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java28
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java10
-rw-r--r--services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java31
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java41
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java2
-rw-r--r--services/core/java/com/android/server/power/Notifier.java2
-rw-r--r--services/core/java/com/android/server/power/TEST_MAPPING4
-rw-r--r--services/core/java/com/android/server/power/WakeLockLog.java101
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java237
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java8
-rw-r--r--services/core/java/com/android/server/power/stats/CpuPowerCalculator.java42
-rw-r--r--services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java23
-rw-r--r--services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java36
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java1
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java18
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputHardwareManager.java10
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java5
-rw-r--r--services/core/java/com/android/server/utils/AlarmQueue.java7
-rw-r--r--services/core/java/com/android/server/vr/VrManagerService.java6
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java24
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java66
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java78
-rw-r--r--services/core/java/com/android/server/wm/AppWarnings.java94
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java28
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java12
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java398
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java17
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java30
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java38
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java29
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java13
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskPersister.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java44
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java35
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java11
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java100
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java10
-rw-r--r--services/java/com/android/server/SystemServer.java30
-rw-r--r--services/tests/InputMethodSystemServerTests/OWNERS1
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java25
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java8
-rw-r--r--services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6bin16856 -> 16852 bytes
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java35
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java5
-rw-r--r--services/tests/displayservicetests/Android.bp22
-rw-r--r--services/tests/displayservicetests/AndroidManifest.xml10
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java)2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java)14
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java)26
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java)16
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java (renamed from services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java (renamed from services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java (renamed from services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java)4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/HbmEventTest.java (renamed from services/tests/servicestests/src/com/android/server/display/HbmEventTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java)86
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeMetadataTest.java (renamed from services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java (renamed from services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java (renamed from services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java)3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java (renamed from services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java)2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java (renamed from services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/TestUtils.java94
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java (renamed from services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/AppSaturationControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/CctEvaluatorTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java)42
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java (renamed from services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java (renamed from services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java (renamed from services/tests/servicestests/src/com/android/server/display/mode/VotesStorageTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/utils/AmbientFilterTest.java (renamed from services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java (renamed from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java)0
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java (renamed from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java)46
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientSensorTest.java (renamed from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java)0
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java238
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java77
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/display/TEST_MAPPING13
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java112
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt3
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt685
-rw-r--r--services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java237
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java78
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java4
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java24
-rw-r--r--services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java13
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java28
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java12
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java14
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java6
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java18
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java6
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java18
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java10
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java204
-rw-r--r--tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java5
-rw-r--r--tests/FlickerTests/Android.bp1
-rw-r--r--tests/FlickerTests/AndroidTestTemplate.xml2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt170
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt19
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml7
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml8
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java34
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java6
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java2
817 files changed, 21781 insertions, 5642 deletions
diff --git a/Android.bp b/Android.bp
index 07e6b36b5d87..040bb6e2a719 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,17 +368,12 @@ java_defaults {
// TODO(b/120066492): remove default_television.xml when the build system
// propagates "required" properly.
"default_television.xml",
- "framework-platform-compat-config",
// TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
// system propagates "required" properly.
"gps_debug.conf",
- "icu4j-platform-compat-config",
"protolog.conf.json.gz",
- "services-platform-compat-config",
- "TeleService-platform-compat-config",
- "documents-ui-compat-config",
- "calendar-provider-compat-config",
- "contacts-provider-platform-compat-config",
+ // any install dependencies should go into framework-minus-apex-install-dependencies
+ // rather than here to avoid bloating incremental build time
],
libs: [
"androidx.annotation_annotation",
@@ -486,6 +481,20 @@ java_library {
apex_available: ["//apex_available:platform"],
}
+java_library {
+ name: "framework-minus-apex-install-dependencies",
+ required: [
+ "framework-minus-apex",
+ "framework-platform-compat-config",
+ "services-platform-compat-config",
+ "icu4j-platform-compat-config",
+ "TeleService-platform-compat-config",
+ "documents-ui-compat-config",
+ "calendar-provider-compat-config",
+ "contacts-provider-platform-compat-config",
+ ],
+}
+
platform_compat_config {
name: "framework-platform-compat-config",
src: ":framework-minus-apex",
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
index 3910a0827aad..7712aeef4161 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
@@ -52,7 +52,7 @@ public class ExpensiveObjectsPerfTest {
}
}
- @Test(timeout = 900)
+ @Test(timeout = 900000)
public void timeClonedDateFormatTimeInstance() {
DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -72,7 +72,7 @@ public class ExpensiveObjectsPerfTest {
}
}
- @Test
+ @Test(timeout = 900000)
public void timeNewCollator() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
diff --git a/apct-tests/perftests/inputmethod/OWNERS b/apct-tests/perftests/inputmethod/OWNERS
index 5deb2ce8f24b..cbd94ba6b467 100644
--- a/apct-tests/perftests/inputmethod/OWNERS
+++ b/apct-tests/perftests/inputmethod/OWNERS
@@ -1 +1,2 @@
+# Bug component: 34867
include /core/java/android/view/inputmethod/OWNERS
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
deleted file mode 100644
index 42fb24f570d2..000000000000
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2019 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.wm;
-
-import static android.perftests.utils.ManualBenchmarkState.StatsReport;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.hamcrest.core.AnyOf.anyOf;
-import static org.hamcrest.core.Is.is;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.perftests.utils.ManualBenchmarkState;
-import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
-import android.perftests.utils.PerfManualStatusReporter;
-import android.util.Pair;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.window.TaskSnapshot;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.lifecycle.Stage;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(Parameterized.class)
-@LargeTest
-public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
- implements ManualBenchmarkState.CustomizedIterationListener {
- private static Intent sRecentsIntent;
-
- @Rule
- public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
-
- @Rule
- public final PerfTestActivityRule mActivityRule =
- new PerfTestActivityRule(true /* launchActivity */);
-
- private long mMeasuredTimeNs;
-
- /**
- * Used to skip each test method if there is error. It cannot be raised in static setup because
- * that will break the amount of target method count.
- */
- private static Exception sSetUpClassException;
-
- @Parameterized.Parameter(0)
- public int intervalBetweenOperations;
-
- @Parameterized.Parameters(name = "interval{0}ms")
- public static Collection<Object[]> getParameters() {
- return Arrays.asList(new Object[][] {
- { 0 },
- { 100 },
- { 300 },
- });
- }
-
- @BeforeClass
- public static void setUpClass() {
- // Get the permission to invoke startRecentsActivity.
- getUiAutomation().adoptShellPermissionIdentity();
-
- final Context context = getInstrumentation().getContext();
- final PackageManager pm = context.getPackageManager();
- final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>());
-
- try {
- final ComponentName recentsComponent =
- ComponentName.unflattenFromString(context.getResources().getString(
- com.android.internal.R.string.config_recentsComponentName));
- final int enabledState = pm.getComponentEnabledSetting(recentsComponent);
- Assume.assumeThat(enabledState, anyOf(
- is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
- is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)));
-
- final boolean homeIsRecents =
- recentsComponent.getPackageName().equals(defaultHome.getPackageName());
- sRecentsIntent =
- new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent);
- } catch (Exception e) {
- sSetUpClassException = e;
- }
- }
-
- @AfterClass
- public static void tearDownClass() {
- sSetUpClassException = null;
- try {
- // Recents activity may stop app switches. Restore the state to avoid affecting
- // the next test.
- ActivityManager.resumeAppSwitches();
- } catch (RemoteException ignored) {
- }
- getUiAutomation().dropShellPermissionIdentity();
- }
-
- @Before
- public void setUp() {
- Assume.assumeNoException(sSetUpClassException);
- }
-
- /** Simulate the timing of touch. */
- private void makeInterval() {
- SystemClock.sleep(intervalBetweenOperations);
- }
-
- /**
- * <pre>
- * Steps:
- * (1) Start recents activity (only make it visible).
- * (2) Finish animation, take turns to execute (a), (b).
- * (a) Move recents activity to top.
- * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP})
- * Move test app to top by startActivityFromRecents.
- * (b) Cancel (it is similar to swipe a little distance and give up to enter recents).
- * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION})
- * (3) Loop (1).
- * </pre>
- */
- @Test
- @ManualBenchmarkTest(
- warmupDurationNs = TIME_1_S_IN_NS,
- targetTestDurationNs = TIME_5_S_IN_NS,
- statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
- | StatsReport.FLAG_COEFFICIENT_VAR))
- public void testRecentsAnimation() throws Throwable {
- final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- state.setCustomizedIterations(getProfilingIterations(), this);
- final IActivityTaskManager atm = ActivityTaskManager.getService();
-
- final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>();
- // Real launch the recents activity.
- finishCases.add(new Pair<>("finishMoveToTop", true));
- // Return to the original top.
- finishCases.add(new Pair<>("finishCancel", false));
-
- // Ensure startRecentsActivity won't be called before finishing the animation.
- final Semaphore recentsSemaphore = new Semaphore(1);
-
- final int testActivityTaskId = mActivityRule.getActivity().getTaskId();
- final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() {
- int mIteration;
-
- @Override
- public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException {
- final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2);
- final boolean moveRecentsToTop = finishCase.second;
- makeInterval();
-
- long startTime = SystemClock.elapsedRealtimeNanos();
- controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */);
- final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime;
- mMeasuredTimeNs += elapsedTimeNsOfFinish;
- state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
-
- if (moveRecentsToTop) {
- mActivityRule.waitForIdleSync(Stage.STOPPED);
-
- startTime = SystemClock.elapsedRealtimeNanos();
- atm.startActivityFromRecents(testActivityTaskId, null /* options */);
- final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
- mMeasuredTimeNs += elapsedTimeNs;
- state.addExtraResult("startFromRecents", elapsedTimeNs);
-
- mActivityRule.waitForIdleSync(Stage.RESUMED);
- }
-
- makeInterval();
- recentsSemaphore.release();
- }
-
- @Override
- public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots)
- throws RemoteException {
- Assume.assumeNoException(
- new AssertionError("onAnimationCanceled should not be called"));
- }
-
- @Override
- public void onTasksAppeared(RemoteAnimationTarget[] app) throws RemoteException {
- /* no-op */
- }
- };
-
- recentsSemaphore.tryAcquire();
- while (state.keepRunning(mMeasuredTimeNs)) {
- mMeasuredTimeNs = 0;
-
- final long startTime = SystemClock.elapsedRealtimeNanos();
- atm.startRecentsActivity(sRecentsIntent, 0 /* eventTime */, anim);
- final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
- mMeasuredTimeNs += elapsedTimeNsOfStart;
- state.addExtraResult("start", elapsedTimeNsOfStart);
-
- // Ensure the animation callback is done.
- Assume.assumeTrue(recentsSemaphore.tryAcquire(
- sIsProfilingMethod() ? 10 * TIME_5_S_IN_NS : TIME_5_S_IN_NS,
- TimeUnit.NANOSECONDS));
- }
- }
-
- @Override
- public void onStart(int iteration) {
- startProfiling(RecentsAnimationPerfTest.class.getSimpleName()
- + "_interval_" + intervalBetweenOperations
- + "_MethodTracing_" + iteration + ".trace");
- }
-
- @Override
- public void onFinished(int iteration) {
- stopProfiling();
- }
-}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 7c8dd92f96d8..9a0053f8add6 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -380,7 +380,7 @@ java_api_library {
":non-updatable-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_stubs_current.from-text",
+ full_api_surface_stub: "android_stubs_current.from-text",
}
java_api_library {
@@ -391,7 +391,7 @@ java_api_library {
":non-updatable-system-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_system_stubs_current.from-text",
+ full_api_surface_stub: "android_system_stubs_current.from-text",
}
java_api_library {
@@ -403,7 +403,7 @@ java_api_library {
":non-updatable-test-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_test_stubs_current.from-text",
+ full_api_surface_stub: "android_test_stubs_current.from-text",
}
java_api_library {
@@ -415,7 +415,7 @@ java_api_library {
":non-updatable-module-lib-current.txt",
],
defaults: ["android-non-updatable_from_text_defaults"],
- dep_api_srcs: "android_module_lib_stubs_current_full.from-text",
+ full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
java_defaults {
diff --git a/core/api/current.txt b/core/api/current.txt
index 00e3effa461c..c2bc5a2426af 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3286,8 +3286,10 @@ package android.accessibilityservice {
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
- method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
- method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
+ method @Deprecated public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
+ method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method @Deprecated public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
+ method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public boolean clearCache();
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public final void disableSelf();
@@ -3399,6 +3401,9 @@ package android.accessibilityservice {
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
+ field public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
+ field public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
+ field public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
field public static final String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
@@ -17437,6 +17442,7 @@ package android.graphics.fonts {
ctor public FontFamily.Builder(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.FontFamily.Builder addFont(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.FontFamily build();
+ method @Nullable public android.graphics.fonts.FontFamily buildVariableFamily();
}
public final class FontStyle {
@@ -17528,17 +17534,21 @@ package android.graphics.text {
public final class LineBreakConfig {
method public int getLineBreakStyle();
method public int getLineBreakWordStyle();
+ method @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig);
field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3
+ field public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff
field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0
field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1
+ field public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1; // 0xffffffff
}
public static final class LineBreakConfig.Builder {
ctor public LineBreakConfig.Builder();
method @NonNull public android.graphics.text.LineBreakConfig build();
+ method @NonNull public android.graphics.text.LineBreakConfig.Builder merge(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int);
method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int);
}
@@ -17613,13 +17623,18 @@ package android.graphics.text {
method public float getAdvance();
method public float getAscent();
method public float getDescent();
+ method public boolean getFakeBold(@IntRange(from=0) int);
+ method public boolean getFakeItalic(@IntRange(from=0) int);
method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
method public float getGlyphX(@IntRange(from=0) int);
method public float getGlyphY(@IntRange(from=0) int);
+ method public float getItalicOverride(@IntRange(from=0) int);
method public float getOffsetX();
method public float getOffsetY();
+ method public float getWeightOverride(@IntRange(from=0) int);
method @IntRange(from=0) public int glyphCount();
+ field public static final float NO_OVERRIDE = 1.4E-45f;
}
public class TextRunShaper {
@@ -48397,6 +48412,11 @@ package android.text.style {
method public void writeToParcel(@NonNull android.os.Parcel, int);
}
+ public class LineBreakConfigSpan {
+ ctor public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig);
+ method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
+ }
+
public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
method public void chooseHeight(CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
@@ -54926,6 +54946,7 @@ package android.view.animation {
public class AnimationUtils {
ctor public AnimationUtils();
method public static long currentAnimationTimeMillis();
+ method public static long getExpectedPresentationTimeMillis();
method public static long getExpectedPresentationTimeNanos();
method public static android.view.animation.Animation loadAnimation(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
method public static android.view.animation.Interpolator loadInterpolator(android.content.Context, @AnimRes @InterpolatorRes int) throws android.content.res.Resources.NotFoundException;
@@ -55760,7 +55781,7 @@ package android.view.inputmethod {
field public static final String SERVICE_INTERFACE = "android.view.InputMethod";
field public static final String SERVICE_META_DATA = "android.view.im";
field public static final int SHOW_EXPLICIT = 1; // 0x1
- field public static final int SHOW_FORCED = 2; // 0x2
+ field @Deprecated public static final int SHOW_FORCED = 2; // 0x2
}
public static interface InputMethod.SessionCallback {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 070822ef49ed..cf24f201315c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -15221,7 +15221,7 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@Nullable String);
method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int);
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1dfd4f9fa484..7c3d8fb856a4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -540,6 +540,7 @@ package android.app.admin {
field public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
field public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
field public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
+ field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
}
public class DevicePolicyManager {
@@ -1060,6 +1061,7 @@ package android.content.pm {
method public boolean isMain();
method public boolean isManagedProfile();
method @Deprecated public boolean isPrimary();
+ method public boolean isPrivateProfile();
method public boolean isProfile();
method public boolean isQuietModeEnabled();
method public boolean isRestricted();
@@ -2681,7 +2683,7 @@ package android.os.vibrator.persistence {
method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException;
}
- public static final class VibrationXmlSerializer.SerializationFailedException extends java.lang.IllegalStateException {
+ public static final class VibrationXmlSerializer.SerializationFailedException extends java.lang.RuntimeException {
}
}
@@ -3334,6 +3336,15 @@ package android.text {
field @NonNull public static final android.os.Parcelable.Creator<android.text.FontConfig.NamedFamilyList> CREATOR;
}
+ public class MeasuredParagraph {
+ method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback);
+ }
+
+ public static interface MeasuredParagraph.StyleRunCallback {
+ method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
+ method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
+ }
+
public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher {
ctor public Selection.MemoryTextWatcher();
method public void afterTextChanged(android.text.Editable);
@@ -3659,6 +3670,7 @@ package android.view.accessibility {
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
+ field public static final int UNDEFINED_WINDOW_ID = -1; // 0xffffffff
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index c395e15d23e9..a78b50c68c2d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -81,6 +81,7 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.function.IntConsumer;
/**
* Accessibility services should only be used to assist users with disabilities in using
@@ -785,6 +786,39 @@ public abstract class AccessibilityService extends Service {
public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
"screenshot_timestamp";
+
+ /**
+ * Annotations for result codes of attaching accessibility overlays.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"OVERLAY_RESULT_"},
+ value = {
+ OVERLAY_RESULT_SUCCESS,
+ OVERLAY_RESULT_INTERNAL_ERROR,
+ OVERLAY_RESULT_INVALID,
+ })
+ public @interface AttachOverlayResult {}
+
+ /** Result code indicating the overlay was successfully attached. */
+ public static final int OVERLAY_RESULT_SUCCESS = 0;
+
+ /**
+ * Result code indicating the overlay could not be attached due to an internal
+ * error and not
+ * because of problems with the input.
+ */
+ public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1;
+
+ /**
+ * Result code indicating the overlay could not be attached because the
+ * specified display or
+ * window id was invalid.
+ */
+ public static final int OVERLAY_RESULT_INVALID = 2;
+
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@UnsupportedAppUsage
@@ -3435,75 +3469,154 @@ public abstract class AccessibilityService extends Service {
}
/**
- * <p>Attaches a {@link android.view.SurfaceControl} containing an accessibility
- * overlay to the
- * specified display. This type of overlay should be used for content that does
- * not need to
- * track the location and size of Views in the currently active app e.g. service
- * configuration
- * or general service UI.</p>
- * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}.
- * To embed the View into a {@link android.view.SurfaceControl}, create a
- * {@link android.view.SurfaceControlViewHost} and attach the View using
- * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by
- * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p>
- * <p>To remove this overlay and free the associated
- * resources, use
- * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p>
- * <p>If the specified overlay has already been attached to the specified display
- * this method does nothing.
- * If the specified overlay has already been attached to a previous display this
- * function will transfer the overlay to the new display.
- * Services can attach multiple overlays. Use
- * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>.
- * to coordinate the order of the overlays on screen.</p>
+ * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the
+ * specified display. This type of overlay should be used for content that does not need to
+ * track the location and size of Views in the currently active app e.g. service configuration
+ * or general service UI.
+ *
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
+ * the View into a {@link android.view.SurfaceControl}, create a {@link
+ * android.view.SurfaceControlViewHost} and attach the View using {@link
+ * android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
+ * <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.
+ *
+ * <p>To remove this overlay and free the associated resources, use <code>
+ * new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
+ *
+ * <p>If the specified overlay has already been attached to the specified display this method
+ * does nothing. If the specified overlay has already been attached to a previous display this
+ * function will transfer the overlay to the new display. Services can attach multiple overlays.
+ * Use <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. to
+ * coordinate the order of the overlays on screen.
*
* @param displayId the display to which the SurfaceControl should be attached.
- * @param sc the SurfaceControl containing the overlay content
+ * @param sc the SurfaceControl containing the overlay content
+ *
+ * @deprecated Use
+ * {@link #attachAccessibilityOverlayToDisplay(int, SurfaceControl, Executor, IntConsumer)}
+ * instead.
*/
+ @Deprecated
public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
- final IAccessibilityServiceConnection connection =
- AccessibilityInteractionClient.getConnection(mConnectionId);
- if (connection == null) {
- return;
- }
- try {
- connection.attachAccessibilityOverlayToDisplay(displayId, sc);
- } catch (RemoteException re) {
- re.rethrowFromSystemServer();
- }
+ AccessibilityInteractionClient.getInstance(this)
+ .attachAccessibilityOverlayToDisplay(mConnectionId, displayId, sc, null, null);
+ }
+
+ /**
+ * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the
+ * specified display. This type of overlay should be used for content that does not need to
+ * track the location and size of Views in the currently active app e.g. service configuration
+ * or general service UI.
+ *
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
+ * the View into a {@link android.view.SurfaceControl}, create a {@link
+ * android.view.SurfaceControlViewHost} and attach the View using {@link
+ * android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
+ * <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.
+ *
+ * <p>To remove this overlay and free the associated resources, use <code>
+ * new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
+ *
+ * <p>If the specified overlay has already been attached to the specified display this method
+ * does nothing. If the specified overlay has already been attached to a previous display this
+ * function will transfer the overlay to the new display. Services can attach multiple overlays.
+ * Use <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. to
+ * coordinate the order of the overlays on screen.
+ *
+ * @param displayId the display to which the SurfaceControl should be attached.
+ * @param sc the SurfaceControl containing the overlay content
+ * @param executor Executor on which to run the callback.
+ * @param callback The callback invoked when attaching the overlay has succeeded or failed. The
+ * callback is a {@link java.util.function.IntConsumer} of the result status code.
+ * @see OVERLAY_RESULT_SUCCESS
+ * @see OVERLAY_RESULT_INVALID
+ * @see OVERLAY_RESULT_INTERNAL_ERROR
+ */
+ public void attachAccessibilityOverlayToDisplay(
+ int displayId,
+ @NonNull SurfaceControl sc,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
+ Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
+ AccessibilityInteractionClient.getInstance(this)
+ .attachAccessibilityOverlayToDisplay(
+ mConnectionId, displayId, sc, executor, callback);
}
/**
- * <p>Attaches an accessibility overlay {@link android.view.SurfaceControl} to the
- * specified
- * window. This method should be used when you want the overlay to move and
- * resize as the parent window moves and resizes.</p>
- * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}.
- * To embed the View into a {@link android.view.SurfaceControl}, create a
- * {@link android.view.SurfaceControlViewHost} and attach the View using
- * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by
- * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p>
- * <p>To remove this overlay and free the associated resources, use
- * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p>
- * <p>If the specified overlay has already been attached to the specified window
- * this method does nothing.
- * If the specified overlay has already been attached to a previous window this
- * function will transfer the overlay to the new window.
- * Services can attach multiple overlays. Use
- * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>.
- * to coordinate the order of the overlays on screen.</p>
- *
- * @param accessibilityWindowId The window id, from
- * {@link AccessibilityWindowInfo#getId()}.
- * @param sc the SurfaceControl containing the overlay
- * content
+ * Attaches an accessibility overlay {@link android.view.SurfaceControl} to the specified
+ * window. This method should be used when you want the overlay to move and resize as the parent
+ * window moves and resizes.
+ *
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
+ * the View into a {@link android.view.SurfaceControl}, create a {@link
+ * android.view.SurfaceControlViewHost} and attach the View using {@link
+ * android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
+ * <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.
+ *
+ * <p>To remove this overlay and free the associated resources, use <code>
+ * new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
+ *
+ * <p>If the specified overlay has already been attached to the specified window this method
+ * does nothing. If the specified overlay has already been attached to a previous window this
+ * function will transfer the overlay to the new window. Services can attach multiple overlays.
+ * Use <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. to
+ * coordinate the order of the overlays on screen.
+ *
+ * @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
+ * @param sc the SurfaceControl containing the overlay content
+ *
+ * @deprecated Use
+ * {@link #attachAccessibilityOverlayToWindow(int, SurfaceControl, Executor,IntConsumer)}
+ * instead.
*/
+ @Deprecated
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
- .attachAccessibilityOverlayToWindow(mConnectionId, accessibilityWindowId, sc);
+ .attachAccessibilityOverlayToWindow(
+ mConnectionId, accessibilityWindowId, sc, null, null);
+ }
+
+ /**
+ * Attaches an accessibility overlay {@link android.view.SurfaceControl} to the specified
+ * window. This method should be used when you want the overlay to move and resize as the parent
+ * window moves and resizes.
+ *
+ * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. To embed
+ * the View into a {@link android.view.SurfaceControl}, create a {@link
+ * android.view.SurfaceControlViewHost} and attach the View using {@link
+ * android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by calling
+ * <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.
+ *
+ * <p>To remove this overlay and free the associated resources, use <code>
+ * new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.
+ *
+ * <p>If the specified overlay has already been attached to the specified window this method
+ * does nothing. If the specified overlay has already been attached to a previous window this
+ * function will transfer the overlay to the new window. Services can attach multiple overlays.
+ * Use <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. to
+ * coordinate the order of the overlays on screen.
+ *
+ * @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
+ * @param sc the SurfaceControl containing the overlay content
+ * @param executor Executor on which to run the callback.
+ * @param callback The callback invoked when attaching the overlay has succeeded or failed. The
+ * callback is a {@link java.util.function.IntConsumer} of the result status code.
+ * @see OVERLAY_RESULT_SUCCESS
+ * @see OVERLAY_RESULT_INVALID
+ * @see OVERLAY_RESULT_INTERNAL_ERROR
+ */
+ public void attachAccessibilityOverlayToWindow(
+ int accessibilityWindowId,
+ @NonNull SurfaceControl sc,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
+ Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
+ AccessibilityInteractionClient.getInstance(this)
+ .attachAccessibilityOverlayToWindow(
+ mConnectionId, accessibilityWindowId, sc, executor, callback);
}
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index e99932dc0661..96716dbbaca1 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -157,7 +157,7 @@ interface IAccessibilityServiceConnection {
void setInstalledAndEnabledServices(in List<AccessibilityServiceInfo> infos);
List<AccessibilityServiceInfo> getInstalledAndEnabledServices();
- void attachAccessibilityOverlayToDisplay(int displayId, in SurfaceControl sc);
+ void attachAccessibilityOverlayToDisplay(int interactionId, int displayId, in SurfaceControl sc, IAccessibilityInteractionConnectionCallback callback);
- void attachAccessibilityOverlayToWindow(int accessibilityWindowId, in SurfaceControl sc);
+ void attachAccessibilityOverlayToWindow(int interactionId, int accessibilityWindowId, in SurfaceControl sc, IAccessibilityInteractionConnectionCallback callback);
} \ No newline at end of file
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 70c3d7ae3f82..8ac507c727c7 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1691,7 +1691,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
return 1;
}
// When neither event happens at INFINITE time:
- return (int) (t1 - t2);
+ return t1 - t2 > 0 ? 1 : -1;
}
});
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0bdf0a08f389..02aeac7502fe 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3156,13 +3156,17 @@ public class Activity extends ContextThemeWrapper
/**
* Called by the system when the device configuration changes while your
- * activity is running. Note that this will <em>only</em> be called if
- * you have selected configurations you would like to handle with the
+ * activity is running. Note that this will only be called if you have
+ * selected configurations you would like to handle with the
* {@link android.R.attr#configChanges} attribute in your manifest. If
* any configuration change occurs that is not selected to be reported
* by that attribute, then instead of reporting it the system will stop
* and restart the activity (to have it launched with the new
- * configuration).
+ * configuration). The only exception is if a size-based configuration
+ * is not large enough to be considered significant, in which case the
+ * system will not recreate the activity and will instead call this
+ * method. For details on this see the documentation on
+ * <a href="{@docRoot}guide/topics/resources/runtime-changes.html">size-based config change</a>.
*
* <p>At the time that this function has been called, your Resources
* object will have been updated to return resource values matching the
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f4a29ed3a92e..0e3a6959bed7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -91,6 +91,7 @@ import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -3918,4 +3919,24 @@ public class ApplicationPackageManager extends PackageManager {
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {
+ Objects.requireNonNull(callback);
+ try {
+ mPM.registerPackageMonitorCallback(callback, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) {
+ Objects.requireNonNull(callback);
+ try {
+ mPM.unregisterPackageMonitorCallback(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 4e2b6fa56b17..d189bab85195 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -24,7 +24,6 @@ import android.app.GrantedUriPermission;
import android.app.IApplicationThread;
import android.app.IActivityClientController;
import android.app.IActivityController;
-import android.app.IAppTask;
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
@@ -107,14 +106,6 @@ interface IActivityTaskManager {
in ProfilerInfo profilerInfo, in Bundle options, int userId);
boolean startNextMatchingActivity(in IBinder callingActivity,
in Intent intent, in Bundle options);
-
- /**
- * The DreamActivity has to be started in a special way that does not involve the PackageParser.
- * The DreamActivity is a framework component inserted in the dream application process. Hence,
- * it is not declared in the application's manifest and cannot be parsed. startDreamActivity
- * creates the activity and starts it without reaching out to the PackageParser.
- */
- boolean startDreamActivity(in Intent intent);
int startActivityIntentSender(in IApplicationThread caller,
in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index fbb0748fc01f..63cae63e1e50 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -24,6 +24,8 @@ import android.view.SurfaceControl;
import android.view.WindowContentFrameStats;
import android.view.WindowAnimationFrameStats;
import android.os.ParcelFileDescriptor;
+import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.LayerCaptureArgs;
import java.util.List;
@@ -43,8 +45,8 @@ interface IUiAutomationConnection {
void injectInputEventToInputFilter(in InputEvent event);
void syncInputTransactions(boolean waitForAnimations);
boolean setRotation(int rotation);
- Bitmap takeScreenshot(in Rect crop);
- Bitmap takeSurfaceControlScreenshot(in SurfaceControl surfaceControl);
+ boolean takeScreenshot(in Rect crop, in ScreenCaptureListener listener);
+ boolean takeSurfaceControlScreenshot(in SurfaceControl surfaceControl, in ScreenCaptureListener listener);
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 785470f2f22e..79b68c1456c7 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -571,6 +571,12 @@ public class NotificationManager {
*/
public static final int BUBBLE_PREFERENCE_SELECTED = 2;
+ /**
+ * Maximum length of the component name of a registered NotificationListenerService.
+ * @hide
+ */
+ public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500;
+
@UnsupportedAppUsage
private static INotificationManager sService;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1e80552f6b82..8647dd29b71a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -382,8 +382,10 @@ public final class SystemServiceRegistry {
registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class,
new CachedServiceFetcher<SelectionToolbarManager>() {
@Override
- public SelectionToolbarManager createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(Context.SELECTION_TOOLBAR_SERVICE);
+ public SelectionToolbarManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.SELECTION_TOOLBAR_SERVICE);
return new SelectionToolbarManager(ctx.getOuterContext(),
ISelectionToolbarManager.Stub.asInterface(b));
}});
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index c4e49954f745..2b5175ca6659 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -242,6 +242,18 @@ public class TaskInfo {
public boolean isLetterboxDoubleTapEnabled;
/**
+ * Whether the user aspect ratio settings button is enabled
+ * @hide
+ */
+ public boolean topActivityEligibleForUserAspectRatioButton;
+
+ /**
+ * Hint about the letterbox state of the top activity.
+ * @hide
+ */
+ public boolean topActivityBoundsLetterboxed;
+
+ /**
* Whether the update comes from a letterbox double-tap action from the user or not.
* @hide
*/
@@ -460,7 +472,8 @@ public class TaskInfo {
public boolean hasCompatUI() {
return hasCameraCompatControl() || topActivityInSizeCompat
|| topActivityEligibleForLetterboxEducation
- || isLetterboxDoubleTapEnabled;
+ || isLetterboxDoubleTapEnabled
+ || topActivityEligibleForUserAspectRatioButton;
}
/**
@@ -510,6 +523,8 @@ public class TaskInfo {
&& supportsMultiWindow == that.supportsMultiWindow
&& displayAreaFeatureId == that.displayAreaFeatureId
&& isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap
+ && topActivityEligibleForUserAspectRatioButton
+ == that.topActivityEligibleForUserAspectRatioButton
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
@@ -543,6 +558,8 @@ public class TaskInfo {
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
&& isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap
+ && topActivityEligibleForUserAspectRatioButton
+ == that.topActivityEligibleForUserAspectRatioButton
&& topActivityEligibleForLetterboxEducation
== that.topActivityEligibleForLetterboxEducation
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
@@ -606,6 +623,8 @@ public class TaskInfo {
displayAreaFeatureId = source.readInt();
cameraCompatControlState = source.readInt();
isLetterboxDoubleTapEnabled = source.readBoolean();
+ topActivityEligibleForUserAspectRatioButton = source.readBoolean();
+ topActivityBoundsLetterboxed = source.readBoolean();
isFromLetterboxDoubleTap = source.readBoolean();
topActivityLetterboxVerticalPosition = source.readInt();
topActivityLetterboxHorizontalPosition = source.readInt();
@@ -660,6 +679,8 @@ public class TaskInfo {
dest.writeInt(displayAreaFeatureId);
dest.writeInt(cameraCompatControlState);
dest.writeBoolean(isLetterboxDoubleTapEnabled);
+ dest.writeBoolean(topActivityEligibleForUserAspectRatioButton);
+ dest.writeBoolean(topActivityBoundsLetterboxed);
dest.writeBoolean(isFromLetterboxDoubleTap);
dest.writeInt(topActivityLetterboxVerticalPosition);
dest.writeInt(topActivityLetterboxHorizontalPosition);
@@ -701,8 +722,11 @@ public class TaskInfo {
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " topActivityEligibleForLetterboxEducation= "
+ topActivityEligibleForLetterboxEducation
- + " topActivityLetterboxed= " + isLetterboxDoubleTapEnabled
- + " isFromDoubleTap= " + isFromLetterboxDoubleTap
+ + " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled
+ + " topActivityEligibleForUserAspectRatioButton= "
+ + topActivityEligibleForUserAspectRatioButton
+ + " topActivityBoundsLetterboxed= " + topActivityBoundsLetterboxed
+ + " isFromLetterboxDoubleTap= " + isFromLetterboxDoubleTap
+ " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition
+ " topActivityLetterboxHorizontalPosition= "
+ topActivityLetterboxHorizontalPosition
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index b613faefe06d..b0180c1857b6 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -37,6 +37,7 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
@@ -71,6 +72,8 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
+import android.window.ScreenCapture;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1190,17 +1193,12 @@ public final class UiAutomation {
Point displaySize = new Point();
display.getRealSize(displaySize);
- int rotation = display.getRotation();
-
// Take the screenshot
- Bitmap screenShot = null;
+ ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
try {
- // Calling out without a lock held.
- screenShot = mUiAutomationConnection.takeScreenshot(
- new Rect(0, 0, displaySize.x, displaySize.y));
- if (screenShot == null) {
- Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
- + mDisplayId);
+ if (!mUiAutomationConnection.takeScreenshot(
+ new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture)) {
return null;
}
} catch (RemoteException re) {
@@ -1208,10 +1206,23 @@ public final class UiAutomation {
return null;
}
- // Optimization
- screenShot.setHasAlpha(false);
+ final ScreenshotHardwareBuffer screenshotBuffer =
+ syncScreenCapture.getBuffer();
+ Bitmap screenShot = screenshotBuffer.asBitmap();
+ if (screenShot == null) {
+ Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
+ + mDisplayId);
+ return null;
+ }
+ Bitmap swBitmap;
+ try (HardwareBuffer buffer = screenshotBuffer.getHardwareBuffer()) {
+ swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
+ }
+ screenShot.recycle();
- return screenShot;
+ // Optimization
+ swBitmap.setHasAlpha(false);
+ return swBitmap;
}
/**
@@ -1248,12 +1259,27 @@ public final class UiAutomation {
// Apply a sync transaction to ensure SurfaceFlinger is flushed before capturing a
// screenshot.
new SurfaceControl.Transaction().apply(true);
+ ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
try {
- return mUiAutomationConnection.takeSurfaceControlScreenshot(sc);
+ if (!mUiAutomationConnection.takeSurfaceControlScreenshot(sc, syncScreenCapture)) {
+ return null;
+ }
+
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while taking screenshot!", re);
return null;
}
+ ScreenCapture.ScreenshotHardwareBuffer captureBuffer =
+ syncScreenCapture.getBuffer();
+ Bitmap screenShot = captureBuffer.asBitmap();
+ Bitmap swBitmap;
+ try (HardwareBuffer buffer = captureBuffer.getHardwareBuffer()) {
+ swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
+ }
+
+ screenShot.recycle();
+ return swBitmap;
}
/**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 34f0964cf823..52949d6d1fbd 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -25,7 +25,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -51,8 +50,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
-import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import libcore.io.IoUtils;
@@ -224,56 +221,54 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
@Override
- public Bitmap takeScreenshot(Rect crop) {
+ public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener) {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+
final long identity = Binder.clearCallingIdentity();
try {
final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
.setSourceCrop(crop)
.build();
- SynchronousScreenCaptureListener syncScreenCapture =
- ScreenCapture.createSyncCaptureListener();
- mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
- syncScreenCapture);
- final ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.getBuffer();
- return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+ mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs, listener);
} catch (RemoteException re) {
re.rethrowAsRuntimeException();
} finally {
Binder.restoreCallingIdentity(identity);
}
- return null;
+
+ return true;
}
@Nullable
@Override
- public Bitmap takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl) {
+ public boolean takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl,
+ ScreenCapture.ScreenCaptureListener listener) {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
- ScreenCapture.ScreenshotHardwareBuffer captureBuffer;
final long identity = Binder.clearCallingIdentity();
try {
- captureBuffer = ScreenCapture.captureLayers(
+ ScreenCapture.LayerCaptureArgs args =
new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl)
- .setChildrenOnly(false)
- .build());
+ .setChildrenOnly(false)
+ .build();
+ int status = ScreenCapture.captureLayers(args, listener);
+
+ if (status != 0) {
+ return false;
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (captureBuffer == null) {
- return null;
- }
- return captureBuffer.asBitmap();
+ return true;
}
@Override
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 74132a3783be..e3a05af8fa35 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -142,9 +142,13 @@ public final class WallpaperInfo implements Parcelable {
mShowMetadataInPreview = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_showMetadataInPreview,
false);
+
+ // Watch wallpapers support ambient mode by default.
+ final boolean defSupportsAmbientMode =
+ pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
- false);
+ defSupportsAmbientMode);
mShouldUseDefaultUnfoldTransition = sa.getBoolean(
com.android.internal.R.styleable
.Wallpaper_shouldUseDefaultUnfoldTransition, true);
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index aeac59b12a2e..ad0af72c72b4 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -160,6 +160,14 @@ public final class DevicePolicyIdentifiers {
public static final String CROSS_PROFILE_WIDGET_PROVIDER_POLICY = "crossProfileWidgetProvider";
/**
+ * String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+
+ /**
* @hide
*/
public static final String USER_RESTRICTION_PREFIX = "userRestriction_";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index da5e40aedbd2..33b8b03e3258 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -12993,7 +12993,7 @@ public class DevicePolicyManager {
}
/**
- * Called by device owners or profile owners of an organization-owned managed profile to to set
+ * Called by device owners or profile owners of an organization-owned managed profile to set
* a local system update policy. When a new policy is set,
* {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcast.
* <p>
@@ -14787,12 +14787,12 @@ public class DevicePolicyManager {
}
/**
- * Called by the system to find out whether the current user's IME was set by the device/profile
- * owner or the user.
+ * Returns true if the current user's IME was set by an admin.
*
- * @return {@code true} if the user's IME was set by the device or profile owner, {@code false}
- * otherwise.
- * @throws SecurityException if the caller is not the device owner/profile owner.
+ * <p>Requires the caller to be the system server, a device owner or profile owner, or a holder
+ * of the QUERY_ADMIN_POLICY permission.
+ *
+ * @throws SecurityException if the caller is not authorized
*
* @hide
*/
@@ -16585,10 +16585,28 @@ public class DevicePolicyManager {
* {@link #canUsbDataSignalingBeDisabled()} to check whether enabling or disabling USB data
* signaling is supported on the device.
*
+ * Starting from {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, after the USB data signaling
+ * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+ * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+ * successfully set or not. This callback will contain:
+ * <ul>
+ * li> The policy identifier {@link DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY}
+ * <li> The {@link TargetUser} that this policy relates to
+ * <li> The {@link PolicyUpdateResult}, which will be
+ * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+ * reason the policy failed to be set
+ * e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+ * </ul>
+ * If there has been a change to the policy,
+ * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+ * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+ * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+ * will contain the reason why the policy changed.
+ *
* @param enabled whether USB data signaling should be enabled or not.
* @throws SecurityException if the caller is not permitted to set this policy
* @throws IllegalStateException if disabling USB data signaling is not supported or
- * if USB data signaling fails to be enabled/disabled.
+ * if USB data signaling fails to be enabled/disabled.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, conditional = true)
public void setUsbDataSignalingEnabled(boolean enabled) {
@@ -16624,25 +16642,6 @@ public class DevicePolicyManager {
}
/**
- * Called by the system to check whether USB data signaling is currently enabled for this user.
- *
- * @param userId which user to check for.
- * @return {@code true} if USB data signaling is enabled, {@code false} otherwise.
- * @hide
- */
- public boolean isUsbDataSignalingEnabledForUser(@UserIdInt int userId) {
- throwIfParentInstance("isUsbDataSignalingEnabledForUser");
- if (mService != null) {
- try {
- return mService.isUsbDataSignalingEnabledForUser(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return true;
- }
-
- /**
* Returns whether enabling or disabling USB data signaling is supported on the device.
*
* @return {@code true} if the device supports enabling and disabling USB data signaling.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 0e78275fa30b..8dd50f0c42e8 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -250,6 +250,16 @@ public abstract class DevicePolicyManagerInternal {
public abstract ComponentName getProfileOwnerAsUser(@UserIdInt int userId);
/**
+ * Returns the device owner component for the device, or {@code null} if there is not one.
+ *
+ * @deprecated added temporarily to support Android Role permission granting.
+ * Please contact Android Enterprise Device Policy team before calling this function.
+ */
+ @Deprecated
+ @Nullable
+ public abstract ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
+
+ /**
* Returns the user id of the device owner, or {@link UserHandle#USER_NULL} if there is not one.
*/
@UserIdInt
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 003e804831a4..95ec89e5f444 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -565,7 +565,6 @@ interface IDevicePolicyManager {
void setUsbDataSignalingEnabled(String callerPackage, boolean enabled);
boolean isUsbDataSignalingEnabled(String callerPackage);
- boolean isUsbDataSignalingEnabledForUser(int userId);
boolean canUsbDataSignalingBeDisabled();
void setMinimumRequiredWifiSecurityLevel(String callerPackageName, int level);
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 3d76b28a3ccc..d7195a76d873 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -65,7 +65,7 @@ public abstract class BroadcastReceiver {
* thread of your app.
*
* <p>Note on threading: the state inside of this class is not itself
- * thread-safe, however you can use it from any thread if you properly
+ * thread-safe. However, you can use it from any thread if you make
* sure that you do not have races. Typically this means you will hand
* the entire object to another thread, which will be solely responsible
* for setting any results and finally calling {@link #finish()}.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5d076d487b30..6d82922484bc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1413,7 +1413,7 @@ public abstract class Context {
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1501,7 +1501,7 @@ public abstract class Context {
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1812,7 +1812,7 @@ public abstract class Context {
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directory returned by
* {@link #getCacheDir()}.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bf25ae8b43a7..7c9ccbad10a2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -55,6 +55,7 @@ import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.content.IntentSender;
@@ -794,6 +795,11 @@ interface IPackageManager {
void setSplashScreenTheme(String packageName, String themeName, int userId);
+ int getUserMinAspectRatio(String packageName, int userId);
+
+ @EnforcePermission("INSTALL_PACKAGES")
+ void setUserMinAspectRatio(String packageName, int userId, int aspectRatio);
+
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);
@@ -818,4 +824,8 @@ interface IPackageManager {
boolean[] canPackageQuery(String sourcePackageName, in String[] targetPackageNames, int userId);
boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler);
+
+ void registerPackageMonitorCallback(IRemoteCallback callback, int userId);
+
+ void unregisterPackageMonitorCallback(IRemoteCallback callback);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3e442e592e10..679dd48f3713 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3091,10 +3091,6 @@ public class PackageInstaller {
* The update ownership enforcement can only be enabled on initial installation. Set
* this to {@code true} on package update is a no-op.
*
- * Apps may opt themselves out of update ownership by setting the
- * <a href="https://developer.android.com/guide/topics/manifest/manifest-element.html#allowupdateownership">android:alllowUpdateOwnership</a>
- * attribute in their manifest to <code>false</code>.
- *
* Note: To enable the update ownership enforcement, the installer must have the
* {@link android.Manifest.permission#ENFORCE_UPDATE_OWNERSHIP ENFORCE_UPDATE_OWNERSHIP}
* permission.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e5468a58e4be..2948bd9b8868 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -65,6 +65,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -1431,6 +1432,7 @@ public abstract class PackageManager {
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
+ INSTALL_DONT_EXTRACT_BASELINE_PROFILES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1645,6 +1647,13 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
+ /**
+ * Flag parameter for {@link PackageInstaller.SessionParams} to indicate that do not extract
+ * the baseline profiles when parsing the apk
+ * @hide
+ */
+ public static final int INSTALL_DONT_EXTRACT_BASELINE_PROFILES = 1 << 27;
+
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
@@ -2342,6 +2351,64 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
+ /**
+ * App minimum aspect ratio set by the user which will override app-defined aspect ratio.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "USER_MIN_ASPECT_RATIO_" }, value = {
+ USER_MIN_ASPECT_RATIO_UNSET,
+ USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+ USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
+ USER_MIN_ASPECT_RATIO_4_3,
+ USER_MIN_ASPECT_RATIO_16_9,
+ USER_MIN_ASPECT_RATIO_3_2,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserMinAspectRatio {}
+
+ /**
+ * No aspect ratio override has been set by user.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_UNSET = 0;
+
+ /**
+ * Aspect ratio override code: user forces app to split screen aspect ratio. This is adjusted to
+ * half of the screen without the split screen divider.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_SPLIT_SCREEN = 1;
+
+ /**
+ * Aspect ratio override code: user forces app to the aspect ratio of the device display size.
+ * This will be the portrait aspect ratio of the device if the app is portrait or the landscape
+ * aspect ratio of the device if the app is landscape.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_DISPLAY_SIZE = 2;
+
+ /**
+ * Aspect ratio override code: user forces app to 4:3 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_4_3 = 3;
+
+ /**
+ * Aspect ratio override code: user forces app to 16:9 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_16_9 = 4;
+
+ /**
+ * Aspect ratio override code: user forces app to 3:2 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_3_2 = 5;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -2576,6 +2643,13 @@ public abstract class PackageManager {
public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID";
/**
+ * Extra field name for notifying package change event. Currently, it is used by PackageMonitor.
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT =
+ "android.content.pm.extra.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT";
+
+ /**
* Usable by the required verifier as the {@code verificationCode} argument
* for {@link PackageManager#verifyPendingInstall} to indicate that it will
* allow the installation to proceed without any of the optional verifiers
@@ -11039,4 +11113,28 @@ public abstract class PackageManager {
throw new UnsupportedOperationException(
"relinquishUpdateOwnership not implemented in subclass");
}
+
+ /**
+ * Register for notifications of package changes such as install, removal and other events.
+ *
+ * @param callback the callback to register for receiving the change events
+ * @param userId The id of registered user
+ * @hide
+ */
+ public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {
+ throw new UnsupportedOperationException(
+ "registerPackageMonitorCallback not implemented in subclass");
+ }
+
+ /**
+ * Unregister for notifications of package changes such as install, removal and other events.
+ *
+ * @param callback the callback to unregister for receiving the change events
+ * @see #registerPackageMonitorCallback(IRemoteCallback, int)
+ * @hide
+ */
+ public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) {
+ throw new UnsupportedOperationException(
+ "unregisterPackageMonitorCallback not implemented in subclass");
+ }
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 7f42c1a08f9c..9f4459d6591b 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -390,6 +390,10 @@ public class UserInfo implements Parcelable {
return UserManager.isUserTypeCommunalProfile(userType);
}
+ public boolean isPrivateProfile() {
+ return UserManager.isUserTypePrivateProfile(userType);
+ }
+
@UnsupportedAppUsage
public boolean isEnabled() {
return (flags & FLAG_DISABLED) != FLAG_DISABLED;
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index e75aa065d3d3..3b53c25d1d8b 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -23,6 +23,9 @@ import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.JsonReader;
@@ -33,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.security.VerityUtils;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -44,6 +48,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
/**
* Helper class used to compute and validate the location of dex metadata files.
@@ -62,6 +67,11 @@ public class DexMetadataHelper {
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+ private static final String PROFILE_FILE_NAME = "primary.prof";
+ private static final String PROFILE_METADATA_FILE_NAME = "primary.profm";
+ private static final String BASELINE_PROFILE_SOURCE_RELATIVE_PATH = "dexopt/baseline.prof";
+ private static final String BASELINE_PROFILE_METADATA_RELATIVE_PATH = "dexopt/baseline.profm";
+
private DexMetadataHelper() {}
/** Return true if the given file is a dex metadata file. */
@@ -313,4 +323,76 @@ public class DexMetadataHelper {
}
}
+ /**
+ * Extract the baseline profiles from the assets directory in the apk. Then create a
+ * ZIP archive with the (.dm) file extension (e.g. foo.dm from foo.apk) to include the
+ * baseline profiles and put the DexMetadata file in the same directory with the apk.
+ *
+ * @param assetManager The {@link AssetManager} to use.
+ * @param apkPath The path of the apk
+ * @return {@code true} if the extraction is successful. Otherwise, return {@code false}.
+ *
+ * @see #buildDexMetadataPathForApk(String)
+ */
+ public static boolean extractBaselineProfilesToDexMetadataFileFromApk(AssetManager assetManager,
+ String apkPath) {
+ if (!ApkLiteParseUtils.isApkPath(apkPath)) {
+ if (DEBUG) {
+ Log.d(TAG, "It is not an apk file: " + apkPath);
+ }
+ return false;
+ }
+
+ // get the name of the DexMetadata file from the path of the apk
+ final File dmFile = new File(buildDexMetadataPathForApk(apkPath));
+ boolean success = false;
+
+ // load profile and profile metadata from assets directory in the apk
+ try (InputStream profileIs = openStreamFromAssets(assetManager,
+ BASELINE_PROFILE_SOURCE_RELATIVE_PATH);
+ InputStream profileMetadataIs = openStreamFromAssets(assetManager,
+ BASELINE_PROFILE_METADATA_RELATIVE_PATH)) {
+ // Create the zip archive file and write the baseline profiles into it.
+ try (FileOutputStream fos = new FileOutputStream(dmFile)) {
+ try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
+ zipOs.putNextEntry(new ZipEntry(PROFILE_FILE_NAME));
+ zipOs.write(profileIs.readAllBytes());
+ zipOs.closeEntry();
+
+ zipOs.putNextEntry(new ZipEntry(PROFILE_METADATA_FILE_NAME));
+ zipOs.write(profileMetadataIs.readAllBytes());
+ zipOs.closeEntry();
+ success = true;
+ }
+ }
+ } catch (IOException e) {
+ if (DEBUG) {
+ Log.e(TAG, "Extract baseline profiles from apk failed: " + e.getMessage());
+ }
+ } finally {
+ if (!success) {
+ if (dmFile.exists()) {
+ dmFile.delete();
+ }
+ }
+ }
+ return success;
+ }
+
+ /**
+ * Loads an {@link AutoCloseInputStream} from assets with the path.
+ *
+ * @param assetManager The {@link AssetManager} to use.
+ * @param path The source file's relative path.
+ * @return An AutoCloseInputStream in case the file was successfully read.
+ * @throws IOException If anything goes wrong while opening or reading the file.
+ */
+ private static AutoCloseInputStream openStreamFromAssets(AssetManager assetManager, String path)
+ throws IOException {
+ AssetFileDescriptor descriptor = assetManager.openFd(path);
+ // Based on the java doc of AssetFileDescriptor#createInputStream, it will always return
+ // an AutoCloseInputStream. It should be fine we cast it from FileInputStream to
+ // AutoCloseInputStream here.
+ return (AutoCloseInputStream) descriptor.createInputStream();
+ }
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 653e243f5e06..4089cfe91eda 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -15,6 +15,8 @@
*/
package android.content.res;
+import static android.content.res.Resources.ID_NULL;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -386,7 +388,7 @@ public final class ApkAssets {
synchronized (this) {
long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
- XmlResourceParser parser = block.newParser();
+ XmlResourceParser parser = block.newParser(ID_NULL, new Validator());
// If nativeOpenXml doesn't throw, it will always return a valid native pointer,
// which makes newParser always return non-null. But let's be careful.
if (parser == null) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index cc06ea721bd1..b225de402f17 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -16,6 +16,8 @@
package android.content.res;
+import static android.content.res.Resources.ID_NULL;
+
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
import android.annotation.AttrRes;
@@ -1089,7 +1091,7 @@ public final class AssetManager implements AutoCloseable {
public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
throws IOException {
try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
- XmlResourceParser parser = block.newParser();
+ XmlResourceParser parser = block.newParser(ID_NULL, new Validator());
// If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
// a valid native pointer, which makes newParser always return non-null. But let's
// be careful.
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
new file mode 100644
index 000000000000..a6fea6ad750a
--- /dev/null
+++ b/core/java/android/content/res/Element.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.Pools.SimplePool;
+
+import androidx.annotation.StyleableRes;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Defines the string attribute length and child tag count restrictions for a xml element.
+ *
+ * {@hide}
+ */
+public class Element {
+ private static final int DEFAULT_MAX_STRING_ATTR_LENGTH = 32_768;
+ private static final int MAX_POOL_SIZE = 128;
+
+ private static final String ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
+
+ protected static final String TAG_ACTION = "action";
+ protected static final String TAG_ACTIVITY = "activity";
+ protected static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+ protected static final String TAG_ACTIVITY_ALIAS = "activity-alias";
+ protected static final String TAG_APPLICATION = "application";
+ protected static final String TAG_ATTRIBUTION = "attribution";
+ protected static final String TAG_CATEGORY = "category";
+ protected static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+ protected static final String TAG_DATA = "data";
+ protected static final String TAG_EAT_COMMENT = "eat-comment";
+ protected static final String TAG_FEATURE_GROUP = "feature-group";
+ protected static final String TAG_GRANT_URI_PERMISSION = "grant-uri-permission";
+ protected static final String TAG_INSTRUMENTATION = "instrumentation";
+ protected static final String TAG_INTENT = "intent";
+ protected static final String TAG_INTENT_FILTER = "intent-filter";
+ protected static final String TAG_KEY_SETS = "key-sets";
+ protected static final String TAG_LAYOUT = "layout";
+ protected static final String TAG_MANIFEST = "manifest";
+ protected static final String TAG_META_DATA = "meta-data";
+ protected static final String TAG_ORIGINAL_PACKAGE = "original-package";
+ protected static final String TAG_OVERLAY = "overlay";
+ protected static final String TAG_PACKAGE = "package";
+ protected static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+ protected static final String TAG_PATH_PERMISSION = "path-permission";
+ protected static final String TAG_PERMISSION = "permission";
+ protected static final String TAG_PERMISSION_GROUP = "permission-group";
+ protected static final String TAG_PERMISSION_TREE = "permission-tree";
+ protected static final String TAG_PROFILEABLE = "profileable";
+ protected static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+ protected static final String TAG_PROPERTY = "property";
+ protected static final String TAG_PROVIDER = "provider";
+ protected static final String TAG_QUERIES = "queries";
+ protected static final String TAG_RECEIVER = "receiver";
+ protected static final String TAG_RESTRICT_UPDATE = "restrict-update";
+ protected static final String TAG_SCREEN = "screen";
+ protected static final String TAG_SERVICE = "service";
+ protected static final String TAG_SUPPORT_SCREENS = "supports-screens";
+ protected static final String TAG_SUPPORTS_GL_TEXTURE = "supports-gl-texture";
+ protected static final String TAG_SUPPORTS_INPUT = "supports-input";
+ protected static final String TAG_SUPPORTS_SCREENS = "supports-screens";
+ protected static final String TAG_USES_CONFIGURATION = "uses-configuration";
+ protected static final String TAG_USES_FEATURE = "uses-feature";
+ protected static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+ protected static final String TAG_USES_LIBRARY = "uses-library";
+ protected static final String TAG_USES_NATIVE_LIBRARY = "uses-native-library";
+ protected static final String TAG_USES_PERMISSION = "uses-permission";
+ protected static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+ protected static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+ protected static final String TAG_USES_SDK = "uses-sdk";
+ protected static final String TAG_USES_SPLIT = "uses-split";
+
+ protected static final String TAG_ATTR_BACKUP_AGENT = "backupAgent";
+ protected static final String TAG_ATTR_CATEGORY = "category";
+ protected static final String TAG_ATTR_HOST = "host";
+ protected static final String TAG_ATTR_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity";
+ protected static final String TAG_ATTR_MIMETYPE = "mimeType";
+ protected static final String TAG_ATTR_NAME = "name";
+ protected static final String TAG_ATTR_PACKAGE = "package";
+ protected static final String TAG_ATTR_PATH = "path";
+ protected static final String TAG_ATTR_PATH_ADVANCED_PATTERN = "pathAdvancedPattern";
+ protected static final String TAG_ATTR_PATH_PATTERN = "pathPattern";
+ protected static final String TAG_ATTR_PATH_PREFIX = "pathPrefix";
+ protected static final String TAG_ATTR_PATH_SUFFIX = "pathSuffix";
+ protected static final String TAG_ATTR_PARENT_ACTIVITY_NAME = "parentActivityName";
+ protected static final String TAG_ATTR_PERMISSION = "permission";
+ protected static final String TAG_ATTR_PERMISSION_GROUP = "permissionGroup";
+ protected static final String TAG_ATTR_PORT = "port";
+ protected static final String TAG_ATTR_PROCESS = "process";
+ protected static final String TAG_ATTR_READ_PERMISSION = "readPermission";
+ protected static final String TAG_ATTR_REQUIRED_ACCOUNT_TYPE = "requiredAccountType";
+ protected static final String TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_NAME =
+ "requiredSystemPropertyName";
+ protected static final String TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_VALUE =
+ "requiredSystemPropertyValue";
+ protected static final String TAG_ATTR_RESTRICTED_ACCOUNT_TYPE = "restrictedAccountType";
+ protected static final String TAG_ATTR_SCHEME = "scheme";
+ protected static final String TAG_ATTR_SHARED_USER_ID = "sharedUserId";
+ protected static final String TAG_ATTR_TARGET_ACTIVITY = "targetActivity";
+ protected static final String TAG_ATTR_TARGET_NAME = "targetName";
+ protected static final String TAG_ATTR_TARGET_PACKAGE = "targetPackage";
+ protected static final String TAG_ATTR_TARGET_PROCESSES = "targetProcesses";
+ protected static final String TAG_ATTR_TASK_AFFINITY = "taskAffinity";
+ protected static final String TAG_ATTR_VALUE = "value";
+ protected static final String TAG_ATTR_VERSION_NAME = "versionName";
+ protected static final String TAG_ATTR_WRITE_PERMISSION = "writePermission";
+
+ private static final String[] ACTIVITY_STR_ATTR_NAMES = {TAG_ATTR_NAME,
+ TAG_ATTR_PARENT_ACTIVITY_NAME, TAG_ATTR_PERMISSION, TAG_ATTR_PROCESS,
+ TAG_ATTR_TASK_AFFINITY};
+ private static final String[] ACTIVITY_ALIAS_STR_ATTR_NAMES = {TAG_ATTR_NAME,
+ TAG_ATTR_PERMISSION, TAG_ATTR_TARGET_ACTIVITY};
+ private static final String[] APPLICATION_STR_ATTR_NAMES = {TAG_ATTR_BACKUP_AGENT,
+ TAG_ATTR_MANAGE_SPACE_ACTIVITY, TAG_ATTR_NAME, TAG_ATTR_PERMISSION, TAG_ATTR_PROCESS,
+ TAG_ATTR_REQUIRED_ACCOUNT_TYPE, TAG_ATTR_RESTRICTED_ACCOUNT_TYPE,
+ TAG_ATTR_TASK_AFFINITY};
+ private static final String[] DATA_STR_ATTR_NAMES = {TAG_ATTR_SCHEME, TAG_ATTR_HOST,
+ TAG_ATTR_PORT, TAG_ATTR_PATH, TAG_ATTR_PATH_PATTERN, TAG_ATTR_PATH_PREFIX,
+ TAG_ATTR_PATH_SUFFIX, TAG_ATTR_PATH_ADVANCED_PATTERN, TAG_ATTR_MIMETYPE};
+ private static final String[] GRANT_URI_PERMISSION_STR_ATTR_NAMES = {TAG_ATTR_PATH,
+ TAG_ATTR_PATH_PATTERN, TAG_ATTR_PATH_PREFIX};
+ private static final String[] INSTRUMENTATION_STR_ATTR_NAMES = {TAG_ATTR_NAME,
+ TAG_ATTR_TARGET_PACKAGE, TAG_ATTR_TARGET_PROCESSES};
+ private static final String[] MANIFEST_STR_ATTR_NAMES = {TAG_ATTR_PACKAGE,
+ TAG_ATTR_SHARED_USER_ID, TAG_ATTR_VERSION_NAME};
+ private static final String[] OVERLAY_STR_ATTR_NAMES = {TAG_ATTR_CATEGORY,
+ TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_NAME, TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_VALUE,
+ TAG_ATTR_TARGET_PACKAGE, TAG_ATTR_TARGET_NAME};
+ private static final String[] PATH_PERMISSION_STR_ATTR_NAMES = {TAG_ATTR_PATH,
+ TAG_ATTR_PATH_PREFIX, TAG_ATTR_PATH_PATTERN, TAG_ATTR_PERMISSION,
+ TAG_ATTR_READ_PERMISSION, TAG_ATTR_WRITE_PERMISSION};
+ private static final String[] PERMISSION_STR_ATTR_NAMES = {TAG_ATTR_NAME,
+ TAG_ATTR_PERMISSION_GROUP};
+ private static final String[] PROVIDER_STR_ATTR_NAMES = {TAG_ATTR_NAME, TAG_ATTR_PERMISSION,
+ TAG_ATTR_PROCESS, TAG_ATTR_READ_PERMISSION, TAG_ATTR_WRITE_PERMISSION};
+ private static final String[] RECEIVER_SERVICE_STR_ATTR_NAMES = {TAG_ATTR_NAME,
+ TAG_ATTR_PERMISSION, TAG_ATTR_PROCESS};
+ private static final String[] NAME_ATTR = {TAG_ATTR_NAME};
+ private static final String[] NAME_VALUE_ATTRS = {TAG_ATTR_NAME, TAG_ATTR_VALUE};
+
+ private String[] mStringAttrNames = new String[0];
+ private final Map<String, TagCounter> mTagCounters = new ArrayMap<>();
+
+ private String mTag;
+
+ private static final ThreadLocal<SimplePool<Element>> sPool =
+ ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE));
+
+ @NonNull
+ static Element obtain(@NonNull String tag) {
+ Element element = sPool.get().acquire();
+ if (element == null) {
+ element = new Element();
+ }
+ element.init(tag);
+ return element;
+ }
+
+ void recycle() {
+ mStringAttrNames = new String[0];
+ Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator();
+ while (it.hasNext()) {
+ it.next().getValue().recycle();
+ it.remove();
+ }
+ mTag = null;
+ sPool.get().release(this);
+ }
+
+ private void init(String tag) {
+ this.mTag = tag;
+ switch (tag) {
+ case TAG_ACTION:
+ case TAG_CATEGORY:
+ case TAG_PACKAGE:
+ case TAG_PERMISSION_GROUP:
+ case TAG_PERMISSION_TREE:
+ case TAG_SUPPORTS_GL_TEXTURE:
+ case TAG_USES_FEATURE:
+ case TAG_USES_LIBRARY:
+ case TAG_USES_NATIVE_LIBRARY:
+ case TAG_USES_PERMISSION:
+ case TAG_USES_PERMISSION_SDK_23:
+ case TAG_USES_SDK:
+ setStringAttrNames(NAME_ATTR);
+ break;
+ case TAG_ACTIVITY:
+ setStringAttrNames(ACTIVITY_STR_ATTR_NAMES);
+ addTagCounter(1000, TAG_LAYOUT);
+ addTagCounter(8000, TAG_META_DATA);
+ addTagCounter(20000, TAG_INTENT_FILTER);
+ break;
+ case TAG_ACTIVITY_ALIAS:
+ setStringAttrNames(ACTIVITY_ALIAS_STR_ATTR_NAMES);
+ addTagCounter(8000, TAG_META_DATA);
+ addTagCounter(20000, TAG_INTENT_FILTER);
+ break;
+ case TAG_APPLICATION:
+ setStringAttrNames(APPLICATION_STR_ATTR_NAMES);
+ addTagCounter(100, TAG_PROFILEABLE);
+ addTagCounter(100, TAG_USES_NATIVE_LIBRARY);
+ addTagCounter(1000, TAG_RECEIVER);
+ addTagCounter(1000, TAG_SERVICE);
+ addTagCounter(4000, TAG_ACTIVITY_ALIAS);
+ addTagCounter(4000, TAG_USES_LIBRARY);
+ addTagCounter(8000, TAG_PROVIDER);
+ addTagCounter(8000, TAG_META_DATA);
+ addTagCounter(40000, TAG_ACTIVITY);
+ break;
+ case TAG_COMPATIBLE_SCREENS:
+ addTagCounter(4000, TAG_SCREEN);
+ break;
+ case TAG_DATA:
+ setStringAttrNames(DATA_STR_ATTR_NAMES);
+ break;
+ case TAG_GRANT_URI_PERMISSION:
+ setStringAttrNames(GRANT_URI_PERMISSION_STR_ATTR_NAMES);
+ break;
+ case TAG_INSTRUMENTATION:
+ setStringAttrNames(INSTRUMENTATION_STR_ATTR_NAMES);
+ break;
+ case TAG_INTENT:
+ case TAG_INTENT_FILTER:
+ addTagCounter(20000, TAG_ACTION);
+ addTagCounter(40000, TAG_CATEGORY);
+ addTagCounter(40000, TAG_DATA);
+ break;
+ case TAG_MANIFEST:
+ setStringAttrNames(MANIFEST_STR_ATTR_NAMES);
+ addTagCounter(100, TAG_APPLICATION);
+ addTagCounter(100, TAG_OVERLAY);
+ addTagCounter(100, TAG_INSTRUMENTATION);
+ addTagCounter(100, TAG_PERMISSION_GROUP);
+ addTagCounter(100, TAG_PERMISSION_TREE);
+ addTagCounter(100, TAG_SUPPORTS_GL_TEXTURE);
+ addTagCounter(100, TAG_SUPPORTS_SCREENS);
+ addTagCounter(100, TAG_USES_CONFIGURATION);
+ addTagCounter(100, TAG_USES_PERMISSION_SDK_23);
+ addTagCounter(100, TAG_USES_SDK);
+ addTagCounter(200, TAG_COMPATIBLE_SCREENS);
+ addTagCounter(200, TAG_QUERIES);
+ addTagCounter(400, TAG_ATTRIBUTION);
+ addTagCounter(400, TAG_USES_FEATURE);
+ addTagCounter(2000, TAG_PERMISSION);
+ addTagCounter(20000, TAG_USES_PERMISSION);
+ break;
+ case TAG_META_DATA:
+ case TAG_PROPERTY:
+ setStringAttrNames(NAME_VALUE_ATTRS);
+ break;
+ case TAG_OVERLAY:
+ setStringAttrNames(OVERLAY_STR_ATTR_NAMES);
+ break;
+ case TAG_PATH_PERMISSION:
+ setStringAttrNames(PATH_PERMISSION_STR_ATTR_NAMES);
+ break;
+ case TAG_PERMISSION:
+ setStringAttrNames(PERMISSION_STR_ATTR_NAMES);
+ break;
+ case TAG_PROVIDER:
+ setStringAttrNames(PROVIDER_STR_ATTR_NAMES);
+ addTagCounter(100, TAG_GRANT_URI_PERMISSION);
+ addTagCounter(100, TAG_PATH_PERMISSION);
+ addTagCounter(8000, TAG_META_DATA);
+ addTagCounter(20000, TAG_INTENT_FILTER);
+ break;
+ case TAG_QUERIES:
+ addTagCounter(1000, TAG_PACKAGE);
+ addTagCounter(2000, TAG_INTENT);
+ addTagCounter(8000, TAG_PROVIDER);
+ break;
+ case TAG_RECEIVER:
+ case TAG_SERVICE:
+ setStringAttrNames(RECEIVER_SERVICE_STR_ATTR_NAMES);
+ addTagCounter(8000, TAG_META_DATA);
+ addTagCounter(20000, TAG_INTENT_FILTER);
+ break;
+ }
+ }
+
+ private void setStringAttrNames(String[] attrNames) {
+ mStringAttrNames = attrNames;
+ }
+
+ private static String getAttrNamespace(String attrName) {
+ if (attrName.equals(TAG_ATTR_PACKAGE)) {
+ return null;
+ }
+ return ANDROID_NAMESPACE;
+ }
+
+ private static int getAttrStringMaxLength(String attrName) {
+ switch (attrName) {
+ case TAG_ATTR_HOST:
+ case TAG_ATTR_PACKAGE:
+ case TAG_ATTR_PERMISSION_GROUP:
+ case TAG_ATTR_PORT:
+ case TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_VALUE:
+ case TAG_ATTR_SCHEME:
+ case TAG_ATTR_SHARED_USER_ID:
+ case TAG_ATTR_TARGET_PACKAGE:
+ return 256;
+ case TAG_ATTR_MIMETYPE:
+ return 512;
+ case TAG_ATTR_BACKUP_AGENT:
+ case TAG_ATTR_CATEGORY:
+ case TAG_ATTR_MANAGE_SPACE_ACTIVITY:
+ case TAG_ATTR_NAME:
+ case TAG_ATTR_PARENT_ACTIVITY_NAME:
+ case TAG_ATTR_PERMISSION:
+ case TAG_ATTR_PROCESS:
+ case TAG_ATTR_READ_PERMISSION:
+ case TAG_ATTR_REQUIRED_ACCOUNT_TYPE:
+ case TAG_ATTR_RESTRICTED_ACCOUNT_TYPE:
+ case TAG_ATTR_TARGET_ACTIVITY:
+ case TAG_ATTR_TARGET_NAME:
+ case TAG_ATTR_TARGET_PROCESSES:
+ case TAG_ATTR_TASK_AFFINITY:
+ case TAG_ATTR_WRITE_PERMISSION:
+ return 1024;
+ case TAG_ATTR_PATH:
+ case TAG_ATTR_PATH_ADVANCED_PATTERN:
+ case TAG_ATTR_PATH_PATTERN:
+ case TAG_ATTR_PATH_PREFIX:
+ case TAG_ATTR_PATH_SUFFIX:
+ case TAG_ATTR_VERSION_NAME:
+ return 4000;
+ default:
+ return DEFAULT_MAX_STRING_ATTR_LENGTH;
+ }
+ }
+
+ private static int getResStringMaxLength(@StyleableRes int index) {
+ switch (index) {
+ case R.styleable.AndroidManifestData_host:
+ case R.styleable.AndroidManifestData_port:
+ case R.styleable.AndroidManifestData_scheme:
+ return 255;
+ case R.styleable.AndroidManifestData_mimeType:
+ return 512;
+ default:
+ return DEFAULT_MAX_STRING_ATTR_LENGTH;
+ }
+ }
+
+ private void addTagCounter(int max, String tag) {
+ mTagCounters.put(tag, TagCounter.obtain(max));
+ }
+
+ boolean hasChild(String tag) {
+ return mTagCounters.containsKey(tag);
+ }
+
+ void validateStringAttrs(@NonNull XmlPullParser attrs) throws XmlPullParserException {
+ for (int i = 0; i < mStringAttrNames.length; i++) {
+ String attrName = mStringAttrNames[i];
+ String val = attrs.getAttributeValue(getAttrNamespace(attrName), attrName);
+ if (val != null && val.length() > getAttrStringMaxLength(attrName)) {
+ throw new XmlPullParserException("String length limit exceeded for "
+ + "attribute " + attrName + " in " + mTag);
+ }
+ }
+ }
+
+ void validateResStringAttr(@StyleableRes int index, CharSequence stringValue)
+ throws XmlPullParserException {
+ if (stringValue != null && stringValue.length() > getResStringMaxLength(index)) {
+ throw new XmlPullParserException("String length limit exceeded for "
+ + "attribute in " + mTag);
+ }
+ }
+
+ void seen(@NonNull Element element) throws XmlPullParserException {
+ if (mTagCounters.containsKey(element.mTag)) {
+ TagCounter counter = mTagCounters.get(element.mTag);
+ counter.increment();
+ if (!counter.isValid()) {
+ throw new XmlPullParserException("The number of child " + element.mTag
+ + " elements exceeded the max allowed in " + this.mTag);
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/res/TagCounter.java b/core/java/android/content/res/TagCounter.java
new file mode 100644
index 000000000000..0e5510f19635
--- /dev/null
+++ b/core/java/android/content/res/TagCounter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.annotation.NonNull;
+import android.util.Pools.SimplePool;
+
+/**
+ * Counter used to track the number of tags seen during manifest validation.
+ *
+ * {@hide}
+ */
+public class TagCounter {
+ private static final int MAX_POOL_SIZE = 512;
+ private static final int DEFAULT_MAX_COUNT = 512;
+
+ private static final ThreadLocal<SimplePool<TagCounter>> sPool =
+ ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE));
+
+ private int mMaxValue;
+ private int mCount;
+
+ @NonNull
+ static TagCounter obtain(int max) {
+ TagCounter counter = sPool.get().acquire();
+ if (counter == null) {
+ counter = new TagCounter();
+ }
+ counter.setMaxValue(max);
+ return counter;
+ }
+
+ void recycle() {
+ mCount = 0;
+ mMaxValue = DEFAULT_MAX_COUNT;
+ sPool.get().release(this);
+ }
+
+ public TagCounter() {
+ mMaxValue = DEFAULT_MAX_COUNT;
+ mCount = 0;
+ }
+
+ private void setMaxValue(int maxValue) {
+ this.mMaxValue = maxValue;
+ }
+
+ void increment() {
+ mCount += 1;
+ }
+
+ public boolean isValid() {
+ return mCount <= mMaxValue;
+ }
+
+ int value() {
+ return mCount;
+ }
+}
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index a7cd168690b4..690dfcf9619b 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -137,8 +137,10 @@ abstract class ThemedResourceCache<T> {
*/
@UnsupportedAppUsage
public void onConfigurationChange(@Config int configChanges) {
- prune(configChanges);
- mGeneration++;
+ synchronized (this) {
+ pruneLocked(configChanges);
+ mGeneration++;
+ }
}
/**
@@ -214,22 +216,20 @@ abstract class ThemedResourceCache<T> {
* simply prune missing weak references
* @return {@code true} if the cache is completely empty after pruning
*/
- private boolean prune(@Config int configChanges) {
- synchronized (this) {
- if (mThemedEntries != null) {
- for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
- if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
- mThemedEntries.removeAt(i);
- }
+ private boolean pruneLocked(@Config int configChanges) {
+ if (mThemedEntries != null) {
+ for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
+ if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
+ mThemedEntries.removeAt(i);
}
}
+ }
- pruneEntriesLocked(mNullThemedEntries, configChanges);
- pruneEntriesLocked(mUnthemedEntries, configChanges);
+ pruneEntriesLocked(mNullThemedEntries, configChanges);
+ pruneEntriesLocked(mUnthemedEntries, configChanges);
- return mThemedEntries == null && mNullThemedEntries == null
- && mUnthemedEntries == null;
- }
+ return mThemedEntries == null && mNullThemedEntries == null
+ && mUnthemedEntries == null;
}
private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 9eb9cd51284a..2e84636202be 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -35,6 +35,8 @@ import com.android.internal.util.XmlUtils;
import dalvik.system.VMRuntime;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.util.Arrays;
/**
@@ -1397,7 +1399,15 @@ public class TypedArray implements AutoCloseable {
}
return null;
}
- return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
+ CharSequence value = mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
+ if (mXml != null && mXml.mValidator != null) {
+ try {
+ mXml.mValidator.validateAttr(mXml, index, value);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException("Failed to validate resource string: " + e.getMessage());
+ }
+ }
+ return value;
}
/** @hide */
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
new file mode 100644
index 000000000000..daa7dfef3b8a
--- /dev/null
+++ b/core/java/android/content/res/Validator.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import static android.content.res.Element.TAG_MANIFEST;
+
+import android.annotation.NonNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Validates manifest files by ensuring that tag counts and the length of string attributes are
+ * restricted.
+ *
+ * {@hide}
+ */
+public class Validator {
+
+ private static final int MAX_TAG_COUNT = 100000;
+
+ private final ArrayDeque<Element> mElements = new ArrayDeque<>();
+ private final Map<String, TagCounter> mTagCounters = new HashMap<>();
+
+ private void cleanUp() {
+ while (!mElements.isEmpty()) {
+ mElements.pop().recycle();
+ }
+ Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator();
+ while (it.hasNext()) {
+ it.next().getValue().recycle();
+ it.remove();
+ }
+ }
+
+ private void seen(String tag) throws XmlPullParserException {
+ mTagCounters.putIfAbsent(tag, TagCounter.obtain(MAX_TAG_COUNT));
+ TagCounter counter = mTagCounters.get(tag);
+ counter.increment();
+ if (!counter.isValid()) {
+ throw new XmlPullParserException("The number of " + tag
+ + " tags exceeded " + MAX_TAG_COUNT);
+ }
+ }
+
+ /**
+ * Validates the elements and it's attributes as the XmlPullParser traverses the xml.
+ */
+ public void validate(@NonNull XmlPullParser parser) throws XmlPullParserException {
+ int eventType = parser.getEventType();
+ int depth = parser.getDepth();
+ // The mElement size should equal to the parser depth-1 when the parser eventType is
+ // START_TAG. If depth - mElement.size() is larger than 1 then that means
+ // validation for the previous element was skipped so we should skip validation for all
+ // descendant elements as well
+ if (depth > mElements.size() + 1) {
+ return;
+ }
+ if (eventType == XmlPullParser.START_TAG) {
+ try {
+ String tag = parser.getName();
+ // only validate manifests
+ if (depth == 0 && mElements.size() == 0 && !TAG_MANIFEST.equals(tag)) {
+ return;
+ }
+ Element parent = mElements.peek();
+ if (parent == null || parent.hasChild(tag)) {
+ seen(tag);
+ Element element = Element.obtain(tag);
+ element.validateStringAttrs(parser);
+ if (parent != null) {
+ parent.seen(element);
+ }
+ mElements.push(element);
+ }
+ } catch (XmlPullParserException e) {
+ cleanUp();
+ throw e;
+ }
+ } else if (eventType == XmlPullParser.END_TAG && depth == mElements.size()) {
+ mElements.pop().recycle();
+ } else if (eventType == XmlPullParser.END_DOCUMENT) {
+ cleanUp();
+ }
+ }
+
+ /**
+ * Validates the resource string of a manifest tag attribute.
+ */
+ public void validateAttr(@NonNull XmlPullParser parser, int index, CharSequence stringValue)
+ throws XmlPullParserException {
+ if (parser.getDepth() > mElements.size()) {
+ return;
+ }
+ mElements.peek().validateResStringAttr(index, stringValue);
+ }
+}
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index 16fd1f7277a7..3afc830fa14d 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -97,6 +97,18 @@ public final class XmlBlock implements AutoCloseable {
}
/**
+ * Returns a XmlResourceParser that validates the xml using the given validator.
+ */
+ public XmlResourceParser newParser(@AnyRes int resId, Validator validator) {
+ synchronized (this) {
+ if (mNative != 0) {
+ return new Parser(nativeCreateParseState(mNative, resId), this, validator);
+ }
+ return null;
+ }
+ }
+
+ /**
* Reference Error.h UNEXPECTED_NULL
*/
private static final int ERROR_NULL_DOCUMENT = Integer.MIN_VALUE + 8;
@@ -108,12 +120,19 @@ public final class XmlBlock implements AutoCloseable {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class Parser implements XmlResourceParser {
+ Validator mValidator;
+
Parser(long parseState, XmlBlock block) {
mParseState = parseState;
mBlock = block;
block.mOpenCount++;
}
+ Parser(long parseState, XmlBlock block, Validator validator) {
+ this(parseState, block);
+ mValidator = validator;
+ }
+
@AnyRes
public int getSourceResId() {
return nativeGetSourceResId(mParseState);
@@ -329,6 +348,9 @@ public final class XmlBlock implements AutoCloseable {
break;
}
mEventType = ev;
+ if (mValidator != null) {
+ mValidator.validate(this);
+ }
if (ev == END_DOCUMENT) {
// Automatically close the parse when we reach the end of
// a document, since the standard XmlPullParser interface
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index d48e20e128b9..6baf91d720c3 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -34,6 +34,7 @@ import android.hardware.camera2.impl.CameraExtensionUtils;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.os.Binder;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
@@ -48,7 +49,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -346,28 +346,29 @@ public final class CameraExtensionCharacteristics {
}
}
- public long registerClient(Context ctx) {
+ public boolean registerClient(Context ctx, IBinder token) {
synchronized (mLock) {
connectToProxyLocked(ctx);
- if (mProxy != null) {
- try {
- return mProxy.registerClient();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to initialize extension! Extension service does "
- + " not respond!");
- return -1;
- }
- } else {
- return -1;
+ if (mProxy == null) {
+ return false;
}
+
+ try {
+ return mProxy.registerClient(token);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to initialize extension! Extension service does "
+ + " not respond!");
+ }
+
+ return false;
}
}
- public void unregisterClient(long clientId) {
+ public void unregisterClient(IBinder token) {
synchronized (mLock) {
if (mProxy != null) {
try {
- mProxy.unregisterClient(clientId);
+ mProxy.unregisterClient(token);
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize extension! Extension service does"
+ " not respond!");
@@ -438,15 +439,15 @@ public final class CameraExtensionCharacteristics {
/**
* @hide
*/
- public static long registerClient(Context ctx) {
- return CameraExtensionManagerGlobal.get().registerClient(ctx);
+ public static boolean registerClient(Context ctx, IBinder token) {
+ return CameraExtensionManagerGlobal.get().registerClient(ctx, token);
}
/**
* @hide
*/
- public static void unregisterClient(long clientId) {
- CameraExtensionManagerGlobal.get().unregisterClient(clientId);
+ public static void unregisterClient(IBinder token) {
+ CameraExtensionManagerGlobal.get().unregisterClient(token);
}
/**
@@ -564,8 +565,9 @@ public final class CameraExtensionCharacteristics {
*/
public @NonNull List<Integer> getSupportedExtensions() {
ArrayList<Integer> ret = new ArrayList<>();
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getSupportedExtensions:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
return Collections.unmodifiableList(ret);
}
@@ -576,7 +578,7 @@ public final class CameraExtensionCharacteristics {
}
}
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableList(ret);
@@ -599,8 +601,9 @@ public final class CameraExtensionCharacteristics {
* supported device-specific extension
*/
public boolean isPostviewAvailable(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#isPostviewAvailable:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -623,7 +626,7 @@ public final class CameraExtensionCharacteristics {
Log.e(TAG, "Failed to query the extension for postview availability! Extension "
+ "service does not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return false;
@@ -656,9 +659,9 @@ public final class CameraExtensionCharacteristics {
@NonNull
public List<Size> getPostviewSupportedSizes(@Extension int extension,
@NonNull Size captureSize, int format) {
-
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getPostviewSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -719,7 +722,7 @@ public final class CameraExtensionCharacteristics {
+ "service does not respond!");
return Collections.emptyList();
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
}
@@ -756,8 +759,9 @@ public final class CameraExtensionCharacteristics {
// TODO: Revisit this code once the Extension preview processor output format
// ambiguity is resolved in b/169799538.
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -787,7 +791,7 @@ public final class CameraExtensionCharacteristics {
+ " not respond!");
return new ArrayList<>();
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
}
@@ -814,8 +818,9 @@ public final class CameraExtensionCharacteristics {
public @NonNull
List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
try {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -867,7 +872,7 @@ public final class CameraExtensionCharacteristics {
}
}
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
@@ -888,7 +893,6 @@ public final class CameraExtensionCharacteristics {
* @param format device-specific extension output format
* @return the range of estimated minimal and maximal capture latency in milliseconds
* or null if no capture latency info can be provided
- *
* @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} /
* {@link ImageFormat#YUV_420_888}; or unsupported extension.
*/
@@ -903,8 +907,9 @@ public final class CameraExtensionCharacteristics {
throw new IllegalArgumentException("Unsupported format: " + format);
}
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getEstimatedCaptureLatencyRangeMillis:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -952,7 +957,7 @@ public final class CameraExtensionCharacteristics {
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return null;
@@ -968,8 +973,9 @@ public final class CameraExtensionCharacteristics {
* @throws IllegalArgumentException in case of an unsupported extension.
*/
public boolean isCaptureProcessProgressAvailable(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#isCaptureProcessProgressAvailable:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -992,7 +998,7 @@ public final class CameraExtensionCharacteristics {
Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return false;
@@ -1013,8 +1019,9 @@ public final class CameraExtensionCharacteristics {
*/
@NonNull
public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getAvailableCaptureRequestKeys:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -1033,10 +1040,11 @@ public final class CameraExtensionCharacteristics {
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
- extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId));
+ extenders.second.onInit(token, mCameraId,
+ mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
- extenders.second.onDeInit();
+ extenders.second.onDeInit(token);
}
if (captureRequestMeta != null) {
@@ -1067,7 +1075,7 @@ public final class CameraExtensionCharacteristics {
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableSet(ret);
@@ -1092,8 +1100,9 @@ public final class CameraExtensionCharacteristics {
*/
@NonNull
public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getAvailableCaptureResultKeys:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -1111,10 +1120,11 @@ public final class CameraExtensionCharacteristics {
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
- extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId));
+ extenders.second.onInit(token, mCameraId,
+ mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
- extenders.second.onDeInit();
+ extenders.second.onDeInit(token);
}
if (captureResultMeta != null) {
@@ -1126,7 +1136,7 @@ public final class CameraExtensionCharacteristics {
}
CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
Object crKey = CaptureResult.Key.class;
- Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+ Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>) crKey;
ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
resultKeys, /*includeSynthetic*/ true));
@@ -1145,7 +1155,7 @@ public final class CameraExtensionCharacteristics {
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableSet(ret);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 85f8ca66715b..a098362f16aa 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -67,6 +67,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -178,22 +179,20 @@ public final class CameraManager {
boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
mFoldedDeviceState = folded;
- ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
- for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
- DeviceStateListener callback = listener.get();
+ Iterator<WeakReference<DeviceStateListener>> it = mDeviceStateListeners.iterator();
+ while(it.hasNext()) {
+ DeviceStateListener callback = it.next().get();
if (callback != null) {
callback.onDeviceStateChanged(folded);
} else {
- invalidListeners.add(listener);
+ it.remove();
}
}
- if (!invalidListeners.isEmpty()) {
- mDeviceStateListeners.removeAll(invalidListeners);
- }
}
public synchronized void addDeviceStateListener(DeviceStateListener listener) {
listener.onDeviceStateChanged(mFoldedDeviceState);
+ mDeviceStateListeners.removeIf(l -> l.get() == null);
mDeviceStateListeners.add(new WeakReference<>(listener));
}
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index b52c650086f4..3b7d801afd85 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -20,11 +20,13 @@ import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
+import android.os.IBinder;
+
/** @hide */
interface ICameraExtensionsProxyService
{
- long registerClient();
- void unregisterClient(long clientId);
+ boolean registerClient(in IBinder token);
+ void unregisterClient(in IBinder token);
boolean advancedExtensionsSupported();
void initializeSession(in IInitializeSessionCallback cb);
void releaseSession();
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 754f8f6625c9..5a2241820534 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -24,11 +24,13 @@ import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.Size;
import android.hardware.camera2.extension.SizeList;
+import android.os.IBinder;
+
/** @hide */
interface IImageCaptureExtenderImpl
{
- void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
- void onDeInit();
+ void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics);
+ void onDeInit(in IBinder token);
@nullable CaptureStageImpl onPresetSession();
@nullable CaptureStageImpl onEnableSession();
@nullable CaptureStageImpl onDisableSession();
diff --git a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
index 01046d01233c..9ea8a7407049 100644
--- a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
@@ -22,11 +22,13 @@ import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.SizeList;
+import android.os.IBinder;
+
/** @hide */
interface IPreviewExtenderImpl
{
- void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
- void onDeInit();
+ void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics);
+ void onDeInit(in IBinder token);
@nullable CaptureStageImpl onPresetSession();
@nullable CaptureStageImpl onEnableSession();
@nullable CaptureStageImpl onDisableSession();
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
index 13b93a8c5e92..0581ec08a131 100644
--- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -25,13 +25,15 @@ import android.hardware.camera2.extension.LatencyPair;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.OutputSurface;
+import android.os.IBinder;
+
/** @hide */
interface ISessionProcessorImpl
{
- CameraSessionConfig initSession(in String cameraId,
+ CameraSessionConfig initSession(in IBinder token, in String cameraId,
in Map<String, CameraMetadataNative> charsMap, in OutputSurface previewSurface,
in OutputSurface imageCaptureSurface, in OutputSurface postviewSurface);
- void deInitSession();
+ void deInitSession(in IBinder token);
void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
void onCaptureSessionEnd();
int startRepeating(in ICaptureCallback callback);
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 65d4b433f132..ae700a0a3c41 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -60,6 +60,7 @@ import android.media.ImageReader;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
@@ -79,7 +80,6 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
private final Executor mExecutor;
private CameraDevice mCameraDevice;
private final Map<String, CameraMetadataNative> mCharacteristicsMap;
- private final long mExtensionClientId;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
private final CameraExtensionSession.StateCallback mCallbacks;
@@ -90,6 +90,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
private RequestProcessor mRequestProcessor = new RequestProcessor();
private final int mSessionId;
+ private final IBinder mToken;
private Surface mClientRepeatingRequestSurface;
private Surface mClientCaptureSurface;
@@ -114,8 +115,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
@NonNull Map<String, CameraCharacteristics> characteristicsMap,
@NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId)
throws CameraAccessException, RemoteException {
- long clientId = CameraExtensionCharacteristics.registerClient(ctx);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + " : " + sessionId);
+ boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
+ if (!success) {
throw new UnsupportedOperationException("Unsupported extension!");
}
@@ -202,11 +204,10 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
config.getExtension());
extender.init(cameraId, characteristicsMapNative);
-
- CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
- extender, cameraDevice, characteristicsMapNative, repeatingRequestSurface,
+ CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(extender,
+ cameraDevice, characteristicsMapNative, repeatingRequestSurface,
burstCaptureSurface, postviewSurface, config.getStateCallback(),
- config.getExecutor(), sessionId);
+ config.getExecutor(), sessionId, token);
ret.mStatsAggregator.setClientName(ctx.getOpPackageName());
ret.mStatsAggregator.setExtensionType(config.getExtension());
@@ -216,15 +217,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
return ret;
}
- private CameraAdvancedExtensionSessionImpl(long extensionClientId,
- @NonNull IAdvancedExtenderImpl extender,
+ private CameraAdvancedExtensionSessionImpl(@NonNull IAdvancedExtenderImpl extender,
@NonNull CameraDeviceImpl cameraDevice,
Map<String, CameraMetadataNative> characteristicsMap,
@Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
@Nullable Surface postviewSurface,
@NonNull StateCallback callback, @NonNull Executor executor,
- int sessionId) {
- mExtensionClientId = extensionClientId;
+ int sessionId, @NonNull IBinder token) {
mAdvancedExtender = extender;
mCameraDevice = cameraDevice;
mCharacteristicsMap = characteristicsMap;
@@ -240,6 +239,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
mSessionClosed = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
+ mToken = token;
mInterfaceLock = cameraDevice.mInterfaceLock;
mStatsAggregator = new ExtensionSessionStatsAggregator(mCameraDevice.getId(),
@@ -260,7 +260,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
OutputSurface postviewSurface = initializeParcelable(mClientPostviewSurface);
mSessionProcessor = mAdvancedExtender.getSessionProcessor();
- CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
+ CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mToken,
+ mCameraDevice.getId(),
mCharacteristicsMap, previewSurface, captureSurface, postviewSurface);
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
@@ -526,7 +527,11 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
synchronized (mInterfaceLock) {
if (mInitialized) {
try {
- mCaptureSession.stopRepeating();
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: already be closed, nothing else to do
+ }
mSessionProcessor.stopRepeating();
mSessionProcessor.onCaptureSessionEnd();
mSessionClosed = true;
@@ -565,7 +570,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
if (!mSessionClosed) {
mSessionProcessor.onCaptureSessionEnd();
}
- mSessionProcessor.deInitSession();
+ mSessionProcessor.deInitSession(mToken);
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize session processor, extension service"
+ " does not respond!") ;
@@ -573,12 +578,10 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
mSessionProcessor = null;
}
- if (mExtensionClientId >= 0) {
- CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
- }
+ CameraExtensionCharacteristics.unregisterClient(mToken);
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
}
mInitialized = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 9ebef0b59ece..1db4808b6430 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -56,6 +56,7 @@ import android.media.ImageWriter;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.LongSparseArray;
@@ -79,7 +80,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private final Executor mExecutor;
private final CameraDevice mCameraDevice;
- private final long mExtensionClientId;
private final IImageCaptureExtenderImpl mImageExtender;
private final IPreviewExtenderImpl mPreviewExtender;
private final Handler mHandler;
@@ -91,6 +91,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private final Set<CaptureRequest.Key> mSupportedRequestKeys;
private final Set<CaptureResult.Key> mSupportedResultKeys;
private final ExtensionSessionStatsAggregator mStatsAggregator;
+ private final IBinder mToken;
private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
@@ -111,6 +112,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private int mPreviewProcessorType = IPreviewExtenderImpl.PROCESSOR_TYPE_NONE;
private boolean mInitialized;
+ private boolean mSessionClosed;
// Enable/Disable internal preview/(repeating request). Extensions expect
// that preview/(repeating request) is enabled and active at any point in time.
// In case the client doesn't explicitly enable repeating requests, the framework
@@ -135,8 +137,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@NonNull ExtensionSessionConfiguration config,
int sessionId)
throws CameraAccessException, RemoteException {
- long clientId = CameraExtensionCharacteristics.registerClient(ctx);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + " : " + sessionId);
+ boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
+ if (!success) {
throw new UnsupportedOperationException("Unsupported extension!");
}
@@ -224,15 +227,16 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
extenders.first.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
- extenders.first.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
+ extenders.first.onInit(token, cameraId,
+ characteristicsMap.get(cameraId).getNativeMetadata());
extenders.second.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
- extenders.second.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
+ extenders.second.onInit(token, cameraId,
+ characteristicsMap.get(cameraId).getNativeMetadata());
CameraExtensionSessionImpl session = new CameraExtensionSessionImpl(
extenders.second,
extenders.first,
supportedPreviewSizes,
- clientId,
cameraDevice,
repeatingRequestSurface,
burstCaptureSurface,
@@ -240,6 +244,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
config.getStateCallback(),
config.getExecutor(),
sessionId,
+ token,
extensionChars.getAvailableCaptureRequestKeys(config.getExtension()),
extensionChars.getAvailableCaptureResultKeys(config.getExtension()));
@@ -254,7 +259,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
@NonNull IPreviewExtenderImpl previewExtender,
@NonNull List<Size> previewSizes,
- long extensionClientId,
@NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
@Nullable Surface repeatingRequestSurface,
@Nullable Surface burstCaptureSurface,
@@ -262,9 +266,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@NonNull StateCallback callback,
@NonNull Executor executor,
int sessionId,
+ @NonNull IBinder token,
@NonNull Set<CaptureRequest.Key> requestKeys,
@Nullable Set<CaptureResult.Key> resultKeys) {
- mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
mCameraDevice = cameraDevice;
@@ -278,8 +282,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
+ mSessionClosed = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
+ mToken = token;
mSupportedRequestKeys = requestKeys;
mSupportedResultKeys = resultKeys;
mCaptureResultsSupported = !resultKeys.isEmpty();
@@ -775,7 +781,12 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
synchronized (mInterfaceLock) {
if (mInitialized) {
mInternalRepeatingRequestEnabled = false;
- mCaptureSession.stopRepeating();
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: already be closed, nothing else to do
+ mSessionClosed = true;
+ }
ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
try {
@@ -793,13 +804,14 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
Log.e(TAG, "Failed to disable extension! Extension service does not "
+ "respond!");
}
- if (!captureStageList.isEmpty()) {
+ if (!captureStageList.isEmpty() && !mSessionClosed) {
CaptureRequest disableRequest = createRequest(mCameraDevice, captureStageList,
mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
mCaptureSession.capture(disableRequest,
new CloseRequestHandler(mRepeatingRequestImageCallback), mHandler);
}
+ mSessionClosed = true;
mStatsAggregator.commit(/*isFinal*/true); // Commit stats before closing session
mCaptureSession.close();
}
@@ -854,19 +866,22 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mHandlerThread.quit();
try {
- mPreviewExtender.onDeInit();
- mImageExtender.onDeInit();
+ if (!mSessionClosed) {
+ // return value is omitted. nothing can do after session is closed.
+ mPreviewExtender.onDisableSession();
+ mImageExtender.onDisableSession();
+ }
+ mPreviewExtender.onDeInit(mToken);
+ mImageExtender.onDeInit(mToken);
} catch (RemoteException e) {
Log.e(TAG, "Failed to release extensions! Extension service does not"
+ " respond!");
}
- if (mExtensionClientId >= 0) {
- CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
- }
+ CameraExtensionCharacteristics.unregisterClient(mToken);
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
}
mInitialized = false;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 76efce56dcf0..022f3c4c3a20 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1762,6 +1762,24 @@ public final class DisplayManager {
* 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7
*/
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
+
+ /**
+ * Key for new power controller feature flag. If enabled new DisplayPowerController will
+ * be used.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller";
+
+ /**
+ * Key for normal brightness mode controller feature flag.
+ * It enables NormalBrightnessModeController.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER = "use_normal_brightness_mode_controller";
}
/**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2f9c2073cd38..a9c4818393a8 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,6 +52,8 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -158,7 +160,6 @@ import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.RingBuffer;
import org.xmlpull.v1.XmlPullParserException;
@@ -481,53 +482,43 @@ public class InputMethodService extends AbstractInputMethodService {
public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
/**
- * Enum values to be used for {@link #setBackDisposition(int)}.
+ * Enum flag to be used for {@link #setBackDisposition(int)}.
*
* @hide
*/
- @IntDef(prefix = { "BACK_DISPOSITION_" }, value = {
- BACK_DISPOSITION_DEFAULT,
- BACK_DISPOSITION_WILL_NOT_DISMISS,
- BACK_DISPOSITION_WILL_DISMISS,
- BACK_DISPOSITION_ADJUST_NOTHING,
- })
- @Retention(RetentionPolicy.SOURCE)
+ @Retention(SOURCE)
+ @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
+ BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
+ prefix = "BACK_DISPOSITION_")
public @interface BackDispositionMode {}
/**
- * Enum flags to be used for {@link #setImeWindowStatus}, representing the current state of the
- * IME window visibility.
- *
* @hide
+ * The IME is active. It may or may not be visible.
*/
- @IntDef(flag = true, prefix = { "IME_" }, value = {
- IME_ACTIVE,
- IME_VISIBLE,
- IME_VISIBLE_IMPERCEPTIBLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImeWindowVisibility {}
+ public static final int IME_ACTIVE = 0x1;
/**
- * The IME is active. It may or may not be visible.
* @hide
+ * The IME is perceptibly visible to the user.
*/
- public static final int IME_ACTIVE = 0x1;
+ public static final int IME_VISIBLE = 0x2;
/**
- * The IME is perceptibly visible to the user.
* @hide
+ * The IME is active and ready with views but set invisible.
+ * This flag cannot be combined with {@link #IME_VISIBLE}.
*/
- public static final int IME_VISIBLE = 0x2;
+ public static final int IME_INVISIBLE = 0x4;
/**
+ * @hide
* The IME is visible, but not yet perceptible to the user (e.g. fading in)
* by {@link android.view.WindowInsetsController}.
*
* @see InputMethodManager#reportPerceptible
- * @hide
*/
- public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4;
+ public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8;
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
@@ -640,18 +631,9 @@ public class InputMethodService extends AbstractInputMethodService {
int mStatusIcon;
- /**
- * Latest value reported of back disposition mode.
- */
@BackDispositionMode
int mBackDisposition;
- /**
- * Latest value reported of IME window visibility flags.
- */
- @ImeWindowVisibility
- private int mImeWindowVisibility;
-
private Object mLock = new Object();
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
@@ -1228,14 +1210,8 @@ public class InputMethodService extends AbstractInputMethodService {
mImeSurfaceRemoverRunnable = null;
}
- private void setImeWindowStatus(@ImeWindowVisibility int vis,
- @BackDispositionMode int backDisposition) {
- if (vis == mImeWindowVisibility && backDisposition == mBackDisposition) {
- return;
- }
- mImeWindowVisibility = Preconditions.checkFlagsArgument(vis, IME_ACTIVE | IME_VISIBLE);
- mBackDisposition = backDisposition;
- mPrivOps.setImeWindowStatusAsync(mImeWindowVisibility, mBackDisposition);
+ private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
+ mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
}
/** Set region of the keyboard to be avoided from back gesture */
@@ -1909,11 +1885,15 @@ public class InputMethodService extends AbstractInputMethodService {
* @param disposition disposition mode to be set
*/
public void setBackDisposition(@BackDispositionMode int disposition) {
- if (disposition < BACK_DISPOSITION_MIN || disposition > BACK_DISPOSITION_MAX) {
+ if (disposition == mBackDisposition) {
+ return;
+ }
+ if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
return;
}
- setImeWindowStatus(mImeWindowVisibility, disposition);
+ mBackDisposition = disposition;
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
/**
@@ -2887,8 +2867,14 @@ public class InputMethodService extends AbstractInputMethodService {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
+ final int previousImeWindowStatus =
+ (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
+ ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
startViews(prepareWindow(showInput));
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ final int nextImeWindowStatus = mapToImeWindowStatus();
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
+ }
mNavigationBarController.onWindowShown();
// compute visibility
@@ -4099,9 +4085,9 @@ public class InputMethodService extends AbstractInputMethodService {
};
}
- @ImeWindowVisibility
private int mapToImeWindowStatus() {
- return IME_ACTIVE | (mDecorViewVisible ? IME_VISIBLE : 0);
+ return IME_ACTIVE
+ | (isInputViewShown() ? IME_VISIBLE : 0);
}
private boolean isAutomotive() {
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index d7db7c741364..9805ce82dcfc 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -3,4 +3,5 @@ set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
-per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
+# Bug component: 1195602 = per-file *InlineSuggestion*
+per-file *InlineSuggestion* = file:/core/java/android/widget/inline/OWNERS
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index b989488f9015..feeef55a957b 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -5,3 +5,4 @@ include platform/frameworks/base:/services/core/java/com/android/server/net/OWNE
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
+per-file Uri.java,Uri.aidl = varunshah@google.com
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 9e97216ac632..c111138d6550 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -26,8 +26,6 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
-import android.app.ActivityThread;
-import android.app.OnActivityPausedListener;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -1473,17 +1471,11 @@ public final class NfcAdapter {
if (activity == null || intent == null) {
throw new NullPointerException();
}
- if (!activity.isResumed()) {
- throw new IllegalStateException("Foreground dispatch can only be enabled " +
- "when your activity is resumed");
- }
try {
TechListParcel parcel = null;
if (techLists != null && techLists.length > 0) {
parcel = new TechListParcel(techLists);
}
- ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
- mForegroundDispatchListener);
sService.setForegroundDispatch(intent, filters, parcel);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
@@ -1511,25 +1503,8 @@ public final class NfcAdapter {
throw new UnsupportedOperationException();
}
}
- ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
- mForegroundDispatchListener);
- disableForegroundDispatchInternal(activity, false);
- }
-
- OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() {
- @Override
- public void onPaused(Activity activity) {
- disableForegroundDispatchInternal(activity, true);
- }
- };
-
- void disableForegroundDispatchInternal(Activity activity, boolean force) {
try {
sService.setForegroundDispatch(null, null, null);
- if (!force && !activity.isResumed()) {
- throw new IllegalStateException("You must disable foreground dispatching " +
- "while your activity is still resumed");
- }
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5072876a04dc..8596e543a148 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -54,6 +54,7 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.CpuScalingPolicies;
import com.google.android.collect.Lists;
@@ -1008,9 +1009,11 @@ public abstract class BatteryStats {
* @param cluster the index of the CPU cluster.
* @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
- * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
- * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+ * @see com.android.internal.os.CpuScalingPolicies#getPolicies
+ * @see com.android.internal.os.CpuScalingPolicies#getFrequencies
+ * @deprecated Unused except in tests
*/
+ @Deprecated
public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
/**
@@ -1648,11 +1651,9 @@ public abstract class BatteryStats {
public abstract long getNextMaxDailyDeadline();
/**
- * Returns the total number of frequencies across all CPU clusters.
+ * Returns the CPU scaling policies.
*/
- public abstract int getCpuFreqCount();
-
- public abstract long[] getCpuFreqs();
+ public abstract CpuScalingPolicies getCpuScalingPolicies();
public final static class HistoryTag {
public String string;
@@ -3390,8 +3391,8 @@ public abstract class BatteryStats {
* cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
* </pre>
*
- * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
- * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+ * @see com.android.internal.os.CpuScalingPolicies#getPolicies
+ * @see com.android.internal.os.CpuScalingPolicies#getFrequencies
*/
@Nullable
public abstract long[] getSystemServiceTimeAtCpuSpeeds();
@@ -4677,12 +4678,14 @@ public abstract class BatteryStats {
proportionalAttributionCalculator.getProportionalPowerMah(consumer)));
}
- final long[] cpuFreqs = getCpuFreqs();
- if (cpuFreqs != null) {
+ final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
+ if (scalingPolicies != null) {
sb.setLength(0);
- for (int i = 0; i < cpuFreqs.length; ++i) {
- if (i != 0) sb.append(',');
- sb.append(cpuFreqs[i]);
+ for (int policy : scalingPolicies.getPolicies()) {
+ for (int frequency : scalingPolicies.getFrequencies(policy)) {
+ if (sb.length() != 0) sb.append(',');
+ sb.append(frequency);
+ }
}
dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
}
@@ -4994,11 +4997,12 @@ public abstract class BatteryStats {
}
// If the cpuFreqs is null, then don't bother checking for cpu freq times.
- if (cpuFreqs != null) {
+ if (scalingPolicies != null) {
final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
// If total cpuFreqTimes is null, then we don't need to check for
// screenOffCpuFreqTimes.
- if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+ if (cpuFreqTimeMs != null
+ && cpuFreqTimeMs.length == scalingPolicies.getScalingStepCount()) {
sb.setLength(0);
for (int i = 0; i < cpuFreqTimeMs.length; ++i) {
if (i != 0) sb.append(',');
@@ -5018,7 +5022,8 @@ public abstract class BatteryStats {
cpuFreqTimeMs.length, sb.toString());
}
- final long[] timesInFreqMs = new long[getCpuFreqCount()];
+ final long[] timesInFreqMs =
+ new long[getCpuScalingPolicies().getScalingStepCount()];
for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
sb.setLength(0);
@@ -6000,14 +6005,18 @@ public abstract class BatteryStats {
}
}
- final long[] cpuFreqs = getCpuFreqs();
- if (cpuFreqs != null) {
+ final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
+ if (scalingPolicies != null) {
sb.setLength(0);
- sb.append(" CPU freqs:");
- for (int i = 0; i < cpuFreqs.length; ++i) {
- sb.append(' ').append(cpuFreqs[i]);
+ sb.append(" CPU scaling: ");
+ for (int policy : scalingPolicies.getPolicies()) {
+ sb.append(" policy").append(policy).append(':');
+ for (int frequency : scalingPolicies.getFrequencies(policy)) {
+ sb.append(' ').append(frequency);
+ }
}
- pw.println(sb.toString());
+
+ pw.println(sb);
pw.println();
}
@@ -6628,7 +6637,7 @@ public abstract class BatteryStats {
pw.println(sb.toString());
}
- final long[] timesInFreqMs = new long[getCpuFreqCount()];
+ final long[] timesInFreqMs = new long[getCpuScalingPolicies().getScalingStepCount()];
for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
sb.setLength(0);
@@ -8078,12 +8087,13 @@ public abstract class BatteryStats {
proto.write(UidProto.Cpu.USER_DURATION_MS, roundUsToMs(u.getUserCpuTimeUs(which)));
proto.write(UidProto.Cpu.SYSTEM_DURATION_MS, roundUsToMs(u.getSystemCpuTimeUs(which)));
- final long[] cpuFreqs = getCpuFreqs();
- if (cpuFreqs != null) {
+ final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
+ if (scalingPolicies != null) {
final long[] cpuFreqTimeMs = u.getCpuFreqTimes(which);
// If total cpuFreqTimes is null, then we don't need to check for
// screenOffCpuFreqTimes.
- if (cpuFreqTimeMs != null && cpuFreqTimeMs.length == cpuFreqs.length) {
+ if (cpuFreqTimeMs != null
+ && cpuFreqTimeMs.length == scalingPolicies.getScalingStepCount()) {
long[] screenOffCpuFreqTimeMs = u.getScreenOffCpuFreqTimes(which);
if (screenOffCpuFreqTimeMs == null) {
screenOffCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
@@ -8100,8 +8110,9 @@ public abstract class BatteryStats {
}
}
- final long[] timesInFreqMs = new long[getCpuFreqCount()];
- final long[] timesInFreqScreenOffMs = new long[getCpuFreqCount()];
+ final int stepCount = getCpuScalingPolicies().getScalingStepCount();
+ final long[] timesInFreqMs = new long[stepCount];
+ final long[] timesInFreqScreenOffMs = new long[stepCount];
for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
if (!u.getScreenOffCpuFreqTimes(timesInFreqScreenOffMs, procState)) {
@@ -8571,10 +8582,12 @@ public abstract class BatteryStats {
dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());
// CPU frequencies (GLOBAL_CPU_FREQ_DATA)
- final long[] cpuFreqs = getCpuFreqs();
- if (cpuFreqs != null) {
- for (long i : cpuFreqs) {
- proto.write(SystemProto.CPU_FREQUENCY, i);
+ final CpuScalingPolicies scalingPolicies = getCpuScalingPolicies();
+ if (scalingPolicies != null) {
+ for (int policy : scalingPolicies.getPolicies()) {
+ for (int frequency : scalingPolicies.getFrequencies(policy)) {
+ proto.write(SystemProto.CPU_FREQUENCY, frequency);
+ }
}
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index c9073fa4b72c..7664bada2c28 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -112,9 +112,19 @@ public class GraphicsEnvironment {
private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
// Values for ANGLE_GL_DRIVER_SELECTION_VALUES
- private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
- private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
- private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+ private enum AngleDriverChoice {
+ DEFAULT("default"),
+ ANGLE("angle"),
+ NATIVE("native");
+
+ public final String choice;
+
+ AngleDriverChoice(String choice) {
+ this.choice = choice;
+ }
+ }
+
+ private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
@@ -193,15 +203,16 @@ public class GraphicsEnvironment {
}
/**
- * Query to determine if ANGLE should be used
+ * Query to determine the ANGLE driver choice.
*/
- private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+ private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings,
+ String packageName) {
if (TextUtils.isEmpty(packageName)) {
Log.v(TAG, "No package name specified; use the system driver");
- return false;
+ return AngleDriverChoice.DEFAULT;
}
- return shouldUseAngleInternal(context, coreSettings, packageName);
+ return queryAngleChoiceInternal(context, coreSettings, packageName);
}
private int getVulkanVersion(PackageManager pm) {
@@ -422,10 +433,11 @@ public class GraphicsEnvironment {
* forces a choice;
* 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
*/
- private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+ private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle,
+ String packageName) {
// Make sure we have a good package name
if (TextUtils.isEmpty(packageName)) {
- return false;
+ return AngleDriverChoice.DEFAULT;
}
// Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
@@ -440,7 +452,7 @@ public class GraphicsEnvironment {
}
if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
Log.v(TAG, "Turn on ANGLE for all applications.");
- return true;
+ return AngleDriverChoice.ANGLE;
}
// Get the per-application settings lists
@@ -463,7 +475,7 @@ public class GraphicsEnvironment {
+ optInPackages.size() + ", "
+ "number of values: "
+ optInValues.size());
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
// See if this application is listed in the per-application settings list
@@ -471,7 +483,7 @@ public class GraphicsEnvironment {
if (pkgIndex < 0) {
Log.v(TAG, packageName + " is not listed in per-application setting");
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
mAngleOptInIndex = pkgIndex;
@@ -481,14 +493,14 @@ public class GraphicsEnvironment {
Log.v(TAG,
"ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + optInValue + "'");
- if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
- return true;
- } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
- return false;
+ if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) {
+ return AngleDriverChoice.ANGLE;
+ } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) {
+ return AngleDriverChoice.NATIVE;
} else {
// The user either chose default or an invalid value; go with the default driver or what
// the game mode indicates
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
}
@@ -501,10 +513,12 @@ public class GraphicsEnvironment {
final List<ResolveInfo> resolveInfos =
pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
if (resolveInfos.size() != 1) {
- Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ Log.v(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ resolveInfos.size());
- for (ResolveInfo resolveInfo : resolveInfos) {
- Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ if (DEBUG) {
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Log.d(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ }
}
return "";
}
@@ -539,26 +553,48 @@ public class GraphicsEnvironment {
}
/**
- * Determine whether ANGLE should be used, set it up if so, and pass ANGLE details down to
- * the C++ GraphicsEnv class.
- *
- * If ANGLE will be used, GraphicsEnv::setAngleInfo() will be called to enable ANGLE to be
- * properly used.
+ * Determine whether ANGLE should be used, attempt to set up from apk first, if ANGLE can be
+ * set up from apk, pass ANGLE details down to the C++ GraphicsEnv class via
+ * GraphicsEnv::setAngleInfo(). If apk setup fails, attempt to set up to use system ANGLE.
+ * Return false if both fail.
*
- * @param context
- * @param bundle
- * @param pm
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
* @param packageName - package name of the application.
- * @return true: ANGLE setup successfully
- * false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
+ * @return true: can set up to use ANGLE successfully.
+ * false: can not set up to use ANGLE (not on allowlist, ANGLE not present, etc.)
*/
- private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+ private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
String packageName) {
- if (!shouldUseAngle(context, bundle, packageName)) {
+ final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName);
+ if (angleDriverChoice == AngleDriverChoice.DEFAULT) {
return false;
}
+ if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) {
+ nativeSetAngleInfo("", true, packageName, null);
+ return false;
+ }
+
+ return setupAngleFromApk(context, bundle, packageManager, packageName)
+ || setupAngleFromSystem(context, bundle, packageName);
+ }
+
+ /**
+ * Attempt to set up ANGLE from the packaged apk, if the apk can be found, pass ANGLE details to
+ * the C++ GraphicsEnv class.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use ANGLE apk.
+ * false: can not set up to use ANGLE apk (ANGLE apk not present, etc.)
+ */
+ private boolean setupAngleFromApk(Context context, Bundle bundle, PackageManager packageManager,
+ String packageName) {
ApplicationInfo angleInfo = null;
// If the developer has specified a debug package over ADB, attempt to find it
@@ -567,7 +603,7 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE debug package enabled: " + anglePkgName);
try {
// Note the debug package does not have to be pre-installed
- angleInfo = pm.getApplicationInfo(anglePkgName, 0);
+ angleInfo = packageManager.getApplicationInfo(anglePkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
// If the debug package is specified but not found, abort.
Log.v(TAG, "ANGLE debug package '" + anglePkgName + "' not installed");
@@ -577,7 +613,7 @@ public class GraphicsEnvironment {
// Otherwise, check to see if ANGLE is properly installed
if (angleInfo == null) {
- anglePkgName = getAnglePackageName(pm);
+ anglePkgName = getAnglePackageName(packageManager);
if (TextUtils.isEmpty(anglePkgName)) {
Log.v(TAG, "Failed to find ANGLE package.");
return false;
@@ -586,7 +622,7 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE package enabled: " + anglePkgName);
try {
// Production ANGLE libraries must be pre-installed as a system app
- angleInfo = pm.getApplicationInfo(anglePkgName,
+ angleInfo = packageManager.getApplicationInfo(anglePkgName,
PackageManager.MATCH_SYSTEM_ONLY);
} catch (PackageManager.NameNotFoundException e) {
Log.v(TAG, "ANGLE package '" + anglePkgName + "' not installed");
@@ -607,15 +643,39 @@ public class GraphicsEnvironment {
Log.d(TAG, "ANGLE package libs: " + paths);
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the
+ // application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo(paths, packageName, ANGLE_GL_DRIVER_CHOICE_ANGLE, features);
+ nativeSetAngleInfo(paths, false, packageName, features);
return true;
}
/**
+ * Attempt to set up ANGLE from system, if the apk can be found, pass ANGLE details to
+ * the C++ GraphicsEnv class.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use system ANGLE.
+ * false: can not set up to use system ANGLE because it doesn't exist.
+ */
+ private boolean setupAngleFromSystem(Context context, Bundle bundle, String packageName) {
+ final boolean systemAngleSupported = SystemProperties
+ .getBoolean(PROPERTY_RO_ANGLE_SUPPORTED, false);
+ if (!systemAngleSupported) {
+ return false;
+ }
+
+ // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
+ // the application package name and ANGLE features to use.
+ final String[] features = getAngleEglFeatures(context, bundle);
+ nativeSetAngleInfo("system", false, packageName, features);
+ return true;
+ }
+
+ /**
* Determine if the "ANGLE In Use" dialog box should be shown.
*/
private boolean shouldShowAngleInUseDialogBox(Context context) {
@@ -651,7 +711,9 @@ public class GraphicsEnvironment {
final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
final String anglePkg = getAnglePackageName(context.getPackageManager());
- intent.setPackage(anglePkg);
+ if (anglePkg.isEmpty()) {
+ return;
+ }
context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
@@ -890,8 +952,8 @@ public class GraphicsEnvironment {
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, String packageName,
- String devOptIn, String[] features);
+ private static native void nativeSetAngleInfo(String path, boolean useNativeDriver,
+ String packageName, String[] features);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index cc5426631a7b..5c4aa4a233fc 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -52,8 +52,20 @@
],
"name": "FrameworksServicesTests",
"options": [
- { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
- { "include-filter": "com.android.server.power.stats.BatteryStatsTests" }
+ { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
+ ]
+ },
+ {
+ "file_patterns": [
+ "BatteryStats[^/]*\\.java",
+ "BatteryUsageStats[^/]*\\.java",
+ "PowerComponents\\.java",
+ "[^/]*BatteryConsumer[^/]*\\.java"
+ ],
+ "name": "FrameworksServicesTests",
+ "options": [
+ { "include-filter": "com.android.server.power.stats" },
+ { "exclude-filter": "com.android.server.power.stats.BatteryStatsTests" }
]
},
{
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 84dc79b92c55..ba1f979b4514 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -159,6 +159,13 @@ public class UserManager {
@SystemApi
public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
+
+ /**
+ * User type representing a private profile.
+ * @hide
+ */
+ public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
+
/**
* User type representing a generic profile for testing purposes. Only on debuggable builds.
* @hide
@@ -2858,6 +2865,16 @@ public class UserManager {
}
/**
+ * Returns whether the user type is a
+ * {@link UserManager#USER_TYPE_PROFILE_PRIVATE private profile}.
+ *
+ * @hide
+ */
+ public static boolean isUserTypePrivateProfile(@Nullable String userType) {
+ return USER_TYPE_PROFILE_PRIVATE.equals(userType);
+ }
+
+ /**
* @hide
* @deprecated Use {@link #isRestrictedProfile()}
*/
@@ -3146,6 +3163,24 @@ public class UserManager {
}
/**
+ * Checks if the context user is a private profile.
+ *
+ * @return whether the context user is a private profile.
+ *
+ * @see android.os.UserManager#USER_TYPE_PROFILE_PRIVATE
+ * @hide
+ */
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ @SuppressAutoDoc
+ public boolean isPrivateProfile() {
+ return isUserTypePrivateProfile(getProfileType());
+ }
+
+ /**
* Checks if the context user is an ephemeral user.
*
* @return whether the context user is an ephemeral user.
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index 22676276f748..d41a428355ea 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -93,6 +93,16 @@ public final class VibrationXmlParser {
private static final String TAG = "VibrationXmlParser";
/**
+ * The MIME type for a xml holding a vibration.
+ *
+ * <p>This should match the type registered at android.mime.types.
+ *
+ * @hide
+ */
+ public static final String APPLICATION_VIBRATION_XML_MIME_TYPE =
+ "application/vnd.android.haptics.vibration+xml";
+
+ /**
* Allows {@link VibrationEffect} instances created via non-public APIs to be parsed/serialized.
*
* <p>Note that the XML schema for non-public APIs is not backwards compatible. This is intended
@@ -111,6 +121,24 @@ public final class VibrationXmlParser {
public @interface Flags {}
/**
+ * Returns whether this parser supports parsing files of the given MIME type.
+ *
+ * <p>Returns false for {@code null} value.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * @hide
+ */
+ public static boolean isSupportedMimeType(@Nullable String mimeType) {
+ // NOTE: prefer using MimeTypeFilter.matches() if MIME_TYPE_VIBRATION_XML becomes a filter
+ // or if more than one MIME type is supported by this parser.
+ return APPLICATION_VIBRATION_XML_MIME_TYPE.equals(mimeType);
+ }
+
+ /**
* Parses XML content from given input stream into a {@link VibrationEffect}.
*
* <p>This parser fails silently and returns {@code null} if the content of the input stream
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index 554d70339b90..1cdfa4f74dbd 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -81,12 +81,11 @@ public final class VibrationXmlSerializer {
* Serializes a {@link VibrationEffect} to XML and writes output to given {@link Writer}.
*
* <p>This method will only write into the {@link Writer} if the effect can successfully
- * be represented by the XML serialization. It will return {@code false} otherwise, and not
- * write any data.
+ * be represented by the XML serialization. It will throw an exception otherwise.
*
* @throws SerializationFailedException serialization of input effect failed, no data was
- * written into given {@link Writer}
- * @throws IOException error writing to given {@link Writer}
+ * written into given {@link Writer}.
+ * @throws IOException error writing to given {@link Writer}.
*
* @hide
*/
@@ -139,10 +138,14 @@ public final class VibrationXmlSerializer {
/**
* Exception thrown when a {@link VibrationEffect} instance serialization fails.
*
+ * <p>The serialization can fail if a given vibration cannot be represented using the public
+ * format, or if it uses hidden APIs that are not supported for serialization (e.g.
+ * {@link VibrationEffect.WaveformBuilder}).
+ *
* @hide
*/
@TestApi
- public static final class SerializationFailedException extends IllegalStateException {
+ public static final class SerializationFailedException extends RuntimeException {
SerializationFailedException(VibrationEffect effect, Throwable cause) {
super("Serialization failed for vibration effect " + effect, cause);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fd28446f3947..7a0556840275 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12696,6 +12696,26 @@ public final class Settings {
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
/**
+ * The duration in milliseconds of each action, separated by commas. Ex:
+ *
+ * "18000,18000,18000,18000,0"
+ *
+ * See com.android.internal.telephony.data.DataStallRecoveryManager for more info
+ * @hide
+ */
+ public static final String DSRM_DURATION_MILLIS = "dsrm_duration_millis";
+
+ /**
+ * The list of DSRM enabled actions, separated by commas. Ex:
+ *
+ * "true,true,false,true,true"
+ *
+ * See com.android.internal.telephony.data.DataStallRecoveryManager for more info
+ * @hide
+ */
+ public static final String DSRM_ENABLED_ACTIONS = "dsrm_enabled_actions";
+
+ /**
* Whether the wifi data connection should remain active even when higher
* priority networks like Ethernet are active, to keep both networks.
* In the case where higher priority networks are connected, wifi will be
diff --git a/core/java/android/service/autofill/augmented/OWNERS b/core/java/android/service/autofill/augmented/OWNERS
index a08863276da7..4187b10f1ed2 100644
--- a/core/java/android/service/autofill/augmented/OWNERS
+++ b/core/java/android/service/autofill/augmented/OWNERS
@@ -1,9 +1,3 @@
# Bug component: 351486
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+wangqi@google.com
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5e7f5d62e256..9b19937444bd 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -26,7 +26,6 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
import android.app.Activity;
-import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1268,9 +1267,7 @@ public class DreamService extends Service implements Window.Callback {
fetchDreamLabel(this, serviceInfo, isPreviewMode));
try {
- if (!ActivityTaskManager.getService().startDreamActivity(i)) {
- detach();
- }
+ mDreamManager.startDreamActivity(i);
} catch (SecurityException e) {
Log.w(mTag,
"Received SecurityException trying to start DreamActivity. "
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 609425c95b0d..dd8b3deabc01 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -17,6 +17,7 @@
package android.service.dreams;
import android.content.ComponentName;
+import android.content.Intent;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.IBinder;
@@ -45,4 +46,5 @@ interface IDreamManager {
void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
void setSystemDreamComponent(in ComponentName componentName);
void registerDreamOverlayService(in ComponentName componentName);
+ void startDreamActivity(in Intent intent);
}
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index da56b4b2f574..3262f3a9fdfb 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -16,6 +16,7 @@
package android.service.wallpaper;
+import android.app.WallpaperInfo;
import android.graphics.Rect;
import android.service.wallpaper.IWallpaperConnection;
@@ -25,6 +26,7 @@ import android.service.wallpaper.IWallpaperConnection;
oneway interface IWallpaperService {
void attach(IWallpaperConnection connection,
IBinder windowToken, int windowType, boolean isPreview,
- int reqWidth, int reqHeight, in Rect padding, int displayId, int which);
+ int reqWidth, int reqHeight, in Rect padding, int displayId, int which,
+ in WallpaperInfo info);
void detach(IBinder windowToken);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dbc1be141571..cc3a662870bb 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1606,8 +1606,13 @@ public abstract class WallpaperService extends Service {
if (!mDestroyed) {
mDisplayState =
mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getCommittedState();
- boolean displayVisible = Display.isOnState(mDisplayState) && !mIsScreenTurningOn;
- boolean visible = mVisible && displayVisible;
+ boolean displayFullyOn = Display.isOnState(mDisplayState) && !mIsScreenTurningOn;
+ boolean supportsAmbientMode =
+ mIWallpaperEngine.mInfo == null
+ ? false
+ : mIWallpaperEngine.mInfo.supportsAmbientMode();
+ // Report visibility only if display is fully on or wallpaper supports ambient mode.
+ boolean visible = mVisible && (displayFullyOn || supportsAmbientMode);
if (DEBUG) {
Log.v(
TAG,
@@ -2060,7 +2065,7 @@ public abstract class WallpaperService extends Service {
}
private void updateFrozenState(boolean frozenRequested) {
- if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null
+ if (mIWallpaperEngine.mInfo == null
// Procees the unfreeze command in case the wallaper became static while
// being paused.
&& frozenRequested) {
@@ -2355,6 +2360,7 @@ public abstract class WallpaperService extends Service {
final DisplayManager mDisplayManager;
final Display mDisplay;
final WallpaperManager mWallpaperManager;
+ @Nullable final WallpaperInfo mInfo;
Engine mEngine;
@SetWallpaperFlags int mWhich;
@@ -2362,7 +2368,7 @@ public abstract class WallpaperService extends Service {
IWallpaperEngineWrapper(WallpaperService service,
IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
- int displayId, @SetWallpaperFlags int which) {
+ int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info) {
mWallpaperManager = getSystemService(WallpaperManager.class);
mCaller = new HandlerCaller(service, service.onProvideEngineLooper(), this, true);
mConnection = conn;
@@ -2374,6 +2380,7 @@ public abstract class WallpaperService extends Service {
mDisplayPadding.set(padding);
mDisplayId = displayId;
mWhich = which;
+ mInfo = info;
// Create a display context before onCreateEngine.
mDisplayManager = getSystemService(DisplayManager.class);
@@ -2447,8 +2454,7 @@ public abstract class WallpaperService extends Service {
Trace.beginSection("WPMS.mConnection.engineShown");
try {
mConnection.engineShown(this);
- Log.d(TAG, "Wallpaper has updated the surface:"
- + mWallpaperManager.getWallpaperInfo());
+ Log.d(TAG, "Wallpaper has updated the surface:" + mInfo);
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
}
@@ -2702,11 +2708,11 @@ public abstract class WallpaperService extends Service {
@Override
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
- int displayId, @SetWallpaperFlags int which) {
+ int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info) {
Trace.beginSection("WPMS.ServiceWrapper.attach");
IWallpaperEngineWrapper engineWrapper =
new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType,
- isPreview, reqWidth, reqHeight, padding, displayId, which);
+ isPreview, reqWidth, reqHeight, padding, displayId, which, info);
synchronized (mActiveEngines) {
mActiveEngines.put(windowToken, engineWrapper);
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index bedd4097d2db..5b4f195d537b 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -20,6 +20,9 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.text.LineBreakConfig;
@@ -28,6 +31,7 @@ import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
import android.text.Layout.Directions;
+import android.text.style.LineBreakConfigSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;
@@ -57,6 +61,7 @@ import java.util.Arrays;
* MeasuredParagraph is NOT a thread safe object.
* @hide
*/
+@TestApi
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
@@ -73,6 +78,7 @@ public class MeasuredParagraph {
* Recycle the MeasuredParagraph.
*
* Do not call any methods after you call this method.
+ * @hide
*/
public void recycle() {
release();
@@ -126,11 +132,14 @@ public class MeasuredParagraph {
private @Nullable MeasuredText mMeasuredText;
// Following three objects are for avoiding object allocation.
- private @NonNull TextPaint mCachedPaint = new TextPaint();
+ private final @NonNull TextPaint mCachedPaint = new TextPaint();
private @Nullable Paint.FontMetricsInt mCachedFm;
+ private final @NonNull LineBreakConfig.Builder mLineBreakConfigBuilder =
+ new LineBreakConfig.Builder();
/**
* Releases internal buffers.
+ * @hide
*/
public void release() {
reset();
@@ -158,6 +167,7 @@ public class MeasuredParagraph {
* Returns the length of the paragraph.
*
* This is always available.
+ * @hide
*/
public int getTextLength() {
return mTextLength;
@@ -167,6 +177,7 @@ public class MeasuredParagraph {
* Returns the characters to be measured.
*
* This is always available.
+ * @hide
*/
public @NonNull char[] getChars() {
return mCopiedBuffer;
@@ -176,6 +187,7 @@ public class MeasuredParagraph {
* Returns the paragraph direction.
*
* This is always available.
+ * @hide
*/
public @Layout.Direction int getParagraphDir() {
return mParaDir;
@@ -185,6 +197,7 @@ public class MeasuredParagraph {
* Returns the directions.
*
* This is always available.
+ * @hide
*/
public Directions getDirections(@IntRange(from = 0) int start, // inclusive
@IntRange(from = 0) int end) { // exclusive
@@ -202,6 +215,7 @@ public class MeasuredParagraph {
*
* This is available only if the MeasuredParagraph is computed with buildForMeasurement.
* Returns 0 in other cases.
+ * @hide
*/
public @FloatRange(from = 0.0f) float getWholeWidth() {
return mWholeWidth;
@@ -212,6 +226,7 @@ public class MeasuredParagraph {
*
* This is available only if the MeasuredParagraph is computed with buildForMeasurement.
* Returns empty array in other cases.
+ * @hide
*/
public @NonNull FloatArray getWidths() {
return mWidths;
@@ -224,6 +239,7 @@ public class MeasuredParagraph {
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
* Returns empty array in other cases.
+ * @hide
*/
public @NonNull IntArray getSpanEndCache() {
return mSpanEndCache;
@@ -236,6 +252,7 @@ public class MeasuredParagraph {
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
* Returns empty array in other cases.
+ * @hide
*/
public @NonNull IntArray getFontMetrics() {
return mFontMetrics;
@@ -246,6 +263,7 @@ public class MeasuredParagraph {
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
* Returns null in other cases.
+ * @hide
*/
public MeasuredText getMeasuredText() {
return mMeasuredText;
@@ -259,6 +277,7 @@ public class MeasuredParagraph {
*
* @param start the inclusive start offset of the target region in the text
* @param end the exclusive end offset of the target region in the text
+ * @hide
*/
public float getWidth(int start, int end) {
if (mMeasuredText == null) {
@@ -280,6 +299,7 @@ public class MeasuredParagraph {
* at (0, 0).
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ * @hide
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect bounds) {
@@ -290,6 +310,7 @@ public class MeasuredParagraph {
* Retrieves the font metrics for the given range.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ * @hide
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt fmi) {
@@ -300,6 +321,7 @@ public class MeasuredParagraph {
* Returns a width of the character at the offset.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ * @hide
*/
public float getCharWidthAt(@IntRange(from = 0) int offset) {
return mMeasuredText.getCharWidthAt(offset);
@@ -318,6 +340,7 @@ public class MeasuredParagraph {
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
+ * @hide
*/
public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
@IntRange(from = 0) int start,
@@ -343,6 +366,7 @@ public class MeasuredParagraph {
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
+ * @hide
*/
public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
@NonNull CharSequence text,
@@ -361,25 +385,53 @@ public class MeasuredParagraph {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
mt.applyMetricsAffectingSpan(
- paint, null /* lineBreakConfig */, null /* spans */, start, end,
- null /* native builder ptr */);
+ paint, null /* lineBreakConfig */, null /* spans */, null /* lbcSpans */,
+ start, end, null /* native builder ptr */, null);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
int spanEnd;
for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
+ int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ MetricAffectingSpan.class);
+ int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ LineBreakConfigSpan.class);
+ spanEnd = Math.min(maSpanEnd, lbcSpanEnd);
MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
MetricAffectingSpan.class);
+ LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ LineBreakConfigSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
+ lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned,
+ LineBreakConfigSpan.class);
mt.applyMetricsAffectingSpan(
- paint, null /* line break config */, spans, spanStart, spanEnd,
- null /* native builder ptr */);
+ paint, null /* line break config */, spans, lbcSpans, spanStart, spanEnd,
+ null /* native builder ptr */, null);
}
}
return mt;
}
/**
+ * A test interface for observing the style run calculation.
+ * @hide
+ */
+ @TestApi
+ public interface StyleRunCallback {
+ /**
+ * Called when a single style run is identified.
+ */
+ void onAppendStyleRun(@NonNull Paint paint,
+ @Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length,
+ boolean isRtl);
+
+ /**
+ * Called when a single replacement run is identified.
+ */
+ void onAppendReplacementRun(@NonNull Paint paint,
+ @IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width);
+ }
+
+ /**
* Generates new MeasuredParagraph for StaticLayout.
*
* If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -397,6 +449,7 @@ public class MeasuredParagraph {
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
+ * @hide
*/
public static @NonNull MeasuredParagraph buildForStaticLayout(
@NonNull TextPaint paint,
@@ -409,6 +462,57 @@ public class MeasuredParagraph {
boolean computeLayout,
@Nullable MeasuredParagraph hint,
@Nullable MeasuredParagraph recycle) {
+ return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir,
+ hyphenationMode, computeLayout, hint, recycle, null);
+ }
+
+ /**
+ * Generates new MeasuredParagraph for StaticLayout.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param paint the paint to be used for rendering the text.
+ * @param lineBreakConfig the line break configuration for text wrapping.
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param hyphenationMode a hyphenation mode
+ * @param computeLayout true if need to compute full layout, otherwise false.
+ *
+ * @return measured text
+ * @hide
+ */
+ @SuppressLint("ExecutorRegistration")
+ @TestApi
+ @NonNull
+ public static MeasuredParagraph buildForStaticLayoutTest(
+ @NonNull TextPaint paint,
+ @Nullable LineBreakConfig lineBreakConfig,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ int hyphenationMode,
+ boolean computeLayout,
+ @Nullable StyleRunCallback testCallback) {
+ return buildForStaticLayoutInternal(paint, lineBreakConfig, text, start, end, textDir,
+ hyphenationMode, computeLayout, null, null, testCallback);
+ }
+
+ private static @NonNull MeasuredParagraph buildForStaticLayoutInternal(
+ @NonNull TextPaint paint,
+ @Nullable LineBreakConfig lineBreakConfig,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ int hyphenationMode,
+ boolean computeLayout,
+ @Nullable MeasuredParagraph hint,
+ @Nullable MeasuredParagraph recycle,
+ @Nullable StyleRunCallback testCallback) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
final MeasuredText.Builder builder;
@@ -426,23 +530,29 @@ public class MeasuredParagraph {
} else {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, start, end,
- builder);
+ mt.applyMetricsAffectingSpan(paint, lineBreakConfig, null /* spans */, null,
+ start, end, builder, testCallback);
mt.mSpanEndCache.append(end);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply
// styles.
int spanEnd;
for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ int maSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
MetricAffectingSpan.class);
+ int lbcSpanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ LineBreakConfigSpan.class);
+ spanEnd = Math.min(maSpanEnd, lbcSpanEnd);
MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
MetricAffectingSpan.class);
+ LineBreakConfigSpan[] lbcSpans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ LineBreakConfigSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
MetricAffectingSpan.class);
- // TODO: Update line break config with spans.
- mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, spanStart, spanEnd,
- builder);
+ lbcSpans = TextUtils.removeEmptySpans(lbcSpans, mt.mSpanned,
+ LineBreakConfigSpan.class);
+ mt.applyMetricsAffectingSpan(paint, lineBreakConfig, spans, lbcSpans, spanStart,
+ spanEnd, builder, testCallback);
mt.mSpanEndCache.append(spanEnd);
}
}
@@ -519,7 +629,8 @@ public class MeasuredParagraph {
@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
@NonNull TextPaint paint,
- @Nullable MeasuredText.Builder builder) {
+ @Nullable MeasuredText.Builder builder,
+ @Nullable StyleRunCallback testCallback) {
// Use original text. Shouldn't matter.
// TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
// backward compatibility? or Should we initialize them for getFontMetricsInt?
@@ -535,13 +646,17 @@ public class MeasuredParagraph {
} else {
builder.appendReplacementRun(paint, end - start, width);
}
+ if (testCallback != null) {
+ testCallback.onAppendReplacementRun(paint, end - start, width);
+ }
}
private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
@NonNull TextPaint paint,
@Nullable LineBreakConfig config,
- @Nullable MeasuredText.Builder builder) {
+ @Nullable MeasuredText.Builder builder,
+ @Nullable StyleRunCallback testCallback) {
if (mLtrWithoutBidi) {
// If the whole text is LTR direction, just apply whole region.
@@ -552,6 +667,9 @@ public class MeasuredParagraph {
} else {
builder.appendStyleRun(paint, config, end - start, false /* isRtl */);
}
+ if (testCallback != null) {
+ testCallback.onAppendStyleRun(paint, config, end - start, false);
+ }
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
byte level = mLevels.get(start);
@@ -568,6 +686,9 @@ public class MeasuredParagraph {
} else {
builder.appendStyleRun(paint, config, levelEnd - levelStart, isRtl);
}
+ if (testCallback != null) {
+ testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl);
+ }
if (levelEnd == end) {
break;
}
@@ -582,9 +703,11 @@ public class MeasuredParagraph {
@NonNull TextPaint paint,
@Nullable LineBreakConfig lineBreakConfig,
@Nullable MetricAffectingSpan[] spans,
+ @Nullable LineBreakConfigSpan[] lbcSpans,
@IntRange(from = 0) int start, // inclusive, in original text buffer
@IntRange(from = 0) int end, // exclusive, in original text buffer
- @Nullable MeasuredText.Builder builder) {
+ @Nullable MeasuredText.Builder builder,
+ @Nullable StyleRunCallback testCallback) {
mCachedPaint.set(paint);
// XXX paint should not have a baseline shift, but...
mCachedPaint.baselineShift = 0;
@@ -609,6 +732,14 @@ public class MeasuredParagraph {
}
}
+ if (lbcSpans != null) {
+ mLineBreakConfigBuilder.reset(lineBreakConfig);
+ for (LineBreakConfigSpan lbcSpan : lbcSpans) {
+ mLineBreakConfigBuilder.merge(lbcSpan.getLineBreakConfig());
+ }
+ lineBreakConfig = mLineBreakConfigBuilder.build();
+ }
+
final int startInCopiedBuffer = start - mTextStart;
final int endInCopiedBuffer = end - mTextStart;
@@ -618,10 +749,10 @@ public class MeasuredParagraph {
if (replacement != null) {
applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, mCachedPaint,
- builder);
+ builder, testCallback);
} else {
applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, mCachedPaint,
- lineBreakConfig, builder);
+ lineBreakConfig, builder, testCallback);
}
if (needFontMetrics) {
@@ -690,6 +821,7 @@ public class MeasuredParagraph {
/**
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
+ * @hide
*/
public @IntRange(from = 0) int getMemoryUsage() {
return mMeasuredText.getMemoryUsage();
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index f31a690c7774..fd97801208ac 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -329,22 +329,17 @@ public class PrecomputedText implements Spannable {
@Override
public int hashCode() {
// TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals.
- int lineBreakStyle = (mLineBreakConfig != null)
- ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE;
return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(),
mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(),
mPaint.getTextLocales(), mPaint.getTypeface(),
mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir,
- mBreakStrategy, mHyphenationFrequency, lineBreakStyle);
+ mBreakStrategy, mHyphenationFrequency,
+ LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig),
+ LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig));
}
@Override
public String toString() {
- int lineBreakStyle = (mLineBreakConfig != null)
- ? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE;
- int lineBreakWordStyle = (mLineBreakConfig != null)
- ? mLineBreakConfig.getLineBreakWordStyle()
- : LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
return "{"
+ "textSize=" + mPaint.getTextSize()
+ ", textScaleX=" + mPaint.getTextScaleX()
@@ -357,8 +352,9 @@ public class PrecomputedText implements Spannable {
+ ", textDir=" + mTextDir
+ ", breakStrategy=" + mBreakStrategy
+ ", hyphenationFrequency=" + mHyphenationFrequency
- + ", lineBreakStyle=" + lineBreakStyle
- + ", lineBreakWordStyle=" + lineBreakWordStyle
+ + ", lineBreakStyle=" + LineBreakConfig.getResolvedLineBreakStyle(mLineBreakConfig)
+ + ", lineBreakWordStyle="
+ + LineBreakConfig.getResolvedLineBreakWordStyle(mLineBreakConfig)
+ "}";
}
};
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6371da4f3776..ab9cff078ac4 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -25,7 +25,6 @@ import android.graphics.Paint;
import android.graphics.text.LineBreakConfig;
import android.graphics.text.LineBreaker;
import android.os.Build;
-import android.os.SystemProperties;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -33,7 +32,6 @@ import android.text.style.TabStopSpan;
import android.util.Log;
import android.util.Pools.SynchronizedPool;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -75,13 +73,6 @@ public class StaticLayout extends Layout {
* default values.
*/
public final static class Builder {
- // The content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
- private static final int DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE = 3;
-
- // The property of content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
- private static final String PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE =
- "android.phrase.linecount.threshold";
-
private Builder() {}
/**
@@ -440,55 +431,11 @@ public class StaticLayout extends Layout {
*/
@NonNull
public StaticLayout build() {
- reviseLineBreakConfig();
StaticLayout result = new StaticLayout(this);
Builder.recycle(this);
return result;
}
- private void reviseLineBreakConfig() {
- boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking();
- int wordStyle = mLineBreakConfig.getLineBreakWordStyle();
- if (autoPhraseBreaking) {
- if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) {
- if (shouldEnablePhraseBreaking()) {
- mLineBreakConfig = LineBreakConfig.getLineBreakConfig(
- mLineBreakConfig.getLineBreakStyle(),
- LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
- mLineBreakConfig.getAutoPhraseBreaking());
- }
- }
- }
- }
-
- private boolean shouldEnablePhraseBreaking() {
- if (TextUtils.isEmpty(mText) || mWidth <= 0) {
- return false;
- }
- int lineLimit = SystemProperties.getInt(
- PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE,
- DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE);
- double desiredWidth = (double) Layout.getDesiredWidth(mText, mStart,
- mEnd, mPaint, mTextDir);
- int lineCount = (int) Math.ceil(desiredWidth / mWidth);
- if (lineCount > 0 && lineCount <= lineLimit) {
- return true;
- }
- return false;
- }
-
- /**
- * Get the line break word style.
- *
- * @return The current line break word style.
- *
- * @hide
- */
- @VisibleForTesting
- public int getLineBreakWordStyle() {
- return mLineBreakConfig.getLineBreakWordStyle();
- }
-
private CharSequence mText;
private int mStart;
private int mEnd;
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 511c9746c84c..518a5498d6ed 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -743,7 +743,7 @@ public class DateUtils
* @param startMillis the start time in UTC milliseconds
* @param endMillis the end time in UTC milliseconds
* @param flags a bit mask of options
- * @param timeZone the time zone to compute the string in. Use null for local
+ * @param timeZone the id of the time zone to compute the string in. Use null for local
* or if the FORMAT_UTC flag is being used.
*
* @return the formatter with the formatted date/time range appended to the string buffer.
diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
new file mode 100644
index 000000000000..90a79c6b3f94
--- /dev/null
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.annotation.NonNull;
+import android.graphics.text.LineBreakConfig;
+
+import java.util.Objects;
+
+/**
+ * LineBreakSpan for changing line break style of the specific region of the text.
+ */
+public class LineBreakConfigSpan {
+ private final LineBreakConfig mLineBreakConfig;
+
+ /**
+ * Construct a new {@link LineBreakConfigSpan}
+ * @param lineBreakConfig a line break config
+ */
+ public LineBreakConfigSpan(@NonNull LineBreakConfig lineBreakConfig) {
+ mLineBreakConfig = lineBreakConfig;
+ }
+
+ /**
+ * Gets an associated line break config.
+ * @return associated line break config.
+ */
+ public @NonNull LineBreakConfig getLineBreakConfig() {
+ return mLineBreakConfig;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof LineBreakConfigSpan)) return false;
+ LineBreakConfigSpan that = (LineBreakConfigSpan) o;
+ return Objects.equals(mLineBreakConfig, that.mLineBreakConfig);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLineBreakConfig);
+ }
+
+ @Override
+ public String toString() {
+ return "LineBreakConfigSpan{mLineBreakConfig=" + mLineBreakConfig + '}';
+ }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ff7d8bb956ca..827600c83fae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -86,9 +86,6 @@ public class FeatureFlagUtils {
public static final String SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST =
"settings_need_connected_ble_device_for_broadcast";
- /** @hide */
- public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
-
/**
* Enable new language and keyboard settings UI
* @hide
@@ -152,6 +149,13 @@ public class FeatureFlagUtils {
*/
public static final String SETTINGS_BIOMETRICS2_ENROLLMENT = "settings_biometrics2_enrollment";
+ /**
+ * Flag to enable/disable FingerprintSettings v2
+ * @hide
+ */
+ public static final String SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS =
+ "settings_biometrics2_fingerprint";
+
/** Flag to enable/disable entire page in Accessibility -> Hearing aids
* @hide
*/
@@ -225,7 +229,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, "true");
- DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "true");
@@ -243,6 +246,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
+ DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -253,7 +257,6 @@ public class FeatureFlagUtils {
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
- PERSISTENT_FLAGS.add(SETTINGS_AUTO_TEXT_WRAPPING);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_UI);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD);
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index eefb15604bbc..2f14b2537cf9 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -209,6 +209,39 @@ public class LongSparseArray<E> implements Cloneable {
}
/**
+ * Returns the index of the first element whose key is greater than or equal to the given key.
+ *
+ * @param key The key for which to search the array.
+ * @return The smallest {@code index} for which {@code (keyAt(index) >= key)} is
+ * {@code true}, or {@link #size() size} if no such {@code index} exists.
+ * @hide
+ */
+ public int firstIndexOnOrAfter(long key) {
+ if (mGarbage) {
+ gc();
+ }
+ final int index = Arrays.binarySearch(mKeys, 0, size(), key);
+ return (index >= 0) ? index : -index - 1;
+ }
+
+ /**
+ * Returns the index of the last element whose key is less than or equal to the given key.
+ *
+ * @param key The key for which to search the array.
+ * @return The largest {@code index} for which {@code (keyAt(index) <= key)} is
+ * {@code true}, or {@code -1} if no such {@code index} exists.
+ * @hide
+ */
+ public int lastIndexOnOrBefore(long key) {
+ final int index = firstIndexOnOrAfter(key);
+
+ if (index < size() && keyAt(index) == key) {
+ return index;
+ }
+ return index - 1;
+ }
+
+ /**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
diff --git a/core/java/android/util/TimeSparseArray.java b/core/java/android/util/TimeSparseArray.java
deleted file mode 100644
index 1f49fa24a64a..000000000000
--- a/core/java/android/util/TimeSparseArray.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package android.util;
-
-import java.util.Objects;
-
-/**
- * An array that indexes by a long timestamp, representing milliseconds since the epoch.
- * @param <E> The type of values this container maps to a timestamp.
- *
- * {@hide}
- */
-public class TimeSparseArray<E> extends LongSparseArray<E> {
- private static final String TAG = TimeSparseArray.class.getSimpleName();
-
- private boolean mWtfReported;
-
- /**
- * Finds the index of the first element whose timestamp is greater or equal to
- * the given time.
- *
- * @param time The timestamp for which to search the array.
- * @return The smallest {@code index} for which {@code (keyAt(index) >= timeStamp)} is
- * {@code true}, or {@link #size() size} if no such {@code index} exists.
- */
- public int closestIndexOnOrAfter(long time) {
- final int size = size();
- int result = size;
- int lo = 0;
- int hi = size - 1;
- while (lo <= hi) {
- final int mid = lo + ((hi - lo) / 2);
- final long key = keyAt(mid);
-
- if (time > key) {
- lo = mid + 1;
- } else if (time < key) {
- hi = mid - 1;
- result = mid;
- } else {
- return mid;
- }
- }
- return result;
- }
-
- /**
- * {@inheritDoc}
- *
- * <p> This container can store only one value for each timestamp. And so ideally, the caller
- * should ensure that there are no collisions. Reporting a {@link Slog#wtf(String, String)}
- * if that happens, as that will lead to the previous value being overwritten.
- */
- @Override
- public void put(long key, E value) {
- final int index = indexOfKey(key);
- if (index >= 0) {
- final E curValue = valueAt(index);
- if (Objects.equals(curValue, value)) {
- Log.w(TAG, "Overwriting value at " + key + " by equal value " + value);
- } else if (!mWtfReported) {
- Slog.wtf(TAG, "Overwriting value " + curValue + " by " + value + " at " + key);
- mWtfReported = true;
- }
- }
- super.put(key, value);
- }
-
- /**
- * Finds the index of the first element whose timestamp is less than or equal to
- * the given time.
- *
- * @param time The timestamp for which to search the array.
- * @return The largest {@code index} for which {@code (keyAt(index) <= timeStamp)} is
- * {@code true}, or -1 if no such {@code index} exists.
- */
- public int closestIndexOnOrBefore(long time) {
- final int index = closestIndexOnOrAfter(time);
-
- if (index < size() && keyAt(index) == time) {
- return index;
- }
- return index - 1;
- }
-}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 6901b7218741..3e615394f7de 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -1968,21 +1968,42 @@ public final class AccessibilityInteractionController {
}
/** Attaches an accessibility overlay to the specified window. */
- public void attachAccessibilityOverlayToWindowClientThread(SurfaceControl sc) {
+ public void attachAccessibilityOverlayToWindowClientThread(
+ SurfaceControl sc,
+ int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
mHandler.sendMessage(
obtainMessage(
AccessibilityInteractionController
::attachAccessibilityOverlayToWindowUiThread,
this,
- sc));
+ sc,
+ interactionId,
+ callback));
}
- private void attachAccessibilityOverlayToWindowUiThread(SurfaceControl sc) {
+ private void attachAccessibilityOverlayToWindowUiThread(
+ SurfaceControl sc,
+ int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
SurfaceControl parent = mViewRootImpl.getSurfaceControl();
- if (parent.isValid()) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.reparent(sc, parent).apply();
- t.close();
+ if (!parent.isValid()) {
+ try {
+ callback.sendAttachOverlayResult(
+ AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR, interactionId);
+ return;
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.reparent(sc, parent).apply();
+ t.close();
+ try {
+ callback.sendAttachOverlayResult(
+ AccessibilityService.OVERLAY_RESULT_SUCCESS, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
}
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a9a5888207aa..072a7f5ea304 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -93,6 +93,11 @@ interface IWindowManager
* Only use {@link DisplayRotation#mUserRotation} as the display rotation.
*/
const int FIXED_TO_USER_ROTATION_ENABLED = 2;
+ /**
+ * If auto-rotation is not supported, {@link DisplayRotation#mUserRotation} will be used.
+ * Otherwise the behavior is same as {link #FIXED_TO_USER_ROTATION_DISABLED}.
+ */
+ const int FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION = 3;
/**
* ===== NOTICE =====
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5019b85ca503..c1eacb535d57 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -797,7 +797,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
- mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
+ mLastInsets.isRound(), false /* alwaysConsumeSystemBars */,
mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags,
mWindowType, mLastWindowingMode, null /* idSideMap */);
mHost.dispatchWindowInsetsAnimationProgress(insets,
@@ -841,7 +841,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mLastDispatchedState;
}
- @VisibleForTesting
public boolean onStateChanged(InsetsState state) {
boolean stateChanged = false;
if (!CAPTION_ON_SHELL) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index e10184976abe..64411866f020 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -68,10 +68,16 @@ public class InsetsSource implements Parcelable {
*/
public static final int FLAG_INSETS_ROUNDED_CORNER = 1 << 1;
+ /**
+ * Controls whether the insets provided by this source should be forcibly consumed.
+ */
+ public static final int FLAG_FORCE_CONSUMING = 1 << 2;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = "FLAG_", value = {
FLAG_SUPPRESS_SCRIM,
FLAG_INSETS_ROUNDED_CORNER,
+ FLAG_FORCE_CONSUMING,
})
public @interface Flags {}
@@ -328,6 +334,9 @@ public class InsetsSource implements Parcelable {
if ((flags & FLAG_INSETS_ROUNDED_CORNER) != 0) {
joiner.add("INSETS_ROUNDED_CORNER");
}
+ if ((flags & FLAG_FORCE_CONSUMING) != 0) {
+ joiner.add("FORCE_CONSUMING");
+ }
return joiner.toString();
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index dceae90822b8..c13b9ab0abd1 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
import static android.view.InsetsStateProto.DISPLAY_FRAME;
@@ -144,12 +145,18 @@ public class InsetsState implements Parcelable {
boolean[] typeVisibilityMap = new boolean[Type.SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
+ @InsetsType int forceConsumingTypes = 0;
@InsetsType int suppressScrimTypes = 0;
for (int i = mSources.size() - 1; i >= 0; i--) {
final InsetsSource source = mSources.valueAt(i);
+ final @InsetsType int type = source.getType();
+
+ if ((source.getFlags() & InsetsSource.FLAG_FORCE_CONSUMING) != 0) {
+ forceConsumingTypes |= type;
+ }
if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) {
- suppressScrimTypes |= source.getType();
+ suppressScrimTypes |= type;
}
processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
@@ -157,7 +164,7 @@ public class InsetsState implements Parcelable {
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
- if (source.getType() != WindowInsets.Type.ime()) {
+ if (type != WindowInsets.Type.ime()) {
InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null
? ignoringVisibilityState.peekSource(source.getId())
: source;
@@ -178,13 +185,13 @@ public class InsetsState implements Parcelable {
if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
compatInsetsTypes &= ~statusBars();
}
- if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)
- && !alwaysConsumeSystemBars) {
- compatInsetsTypes = 0;
+ if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
+ // Clear all types but forceConsumingTypes.
+ compatInsetsTypes &= forceConsumingTypes;
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, suppressScrimTypes, calculateRelativeCutout(frame),
+ forceConsumingTypes, suppressScrimTypes, calculateRelativeCutout(frame),
calculateRelativeRoundedCorners(frame),
calculateRelativePrivacyIndicatorBounds(frame),
calculateRelativeDisplayShape(frame),
@@ -290,9 +297,8 @@ public class InsetsState implements Parcelable {
public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode,
@SoftInputModeFlags int softInputMode, int windowFlags) {
- if (clearsCompatInsets(windowType, windowFlags, windowingMode)) {
- return Insets.NONE;
- }
+ final boolean clearsCompatInsets = clearsCompatInsets(
+ windowType, windowFlags, windowingMode);
final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
? systemBars() | ime()
@@ -303,6 +309,9 @@ public class InsetsState implements Parcelable {
if ((source.getType() & visibleInsetsTypes) == 0) {
continue;
}
+ if (clearsCompatInsets && !source.hasFlags(FLAG_FORCE_CONSUMING)) {
+ continue;
+ }
insets = Insets.max(source.calculateVisibleInsets(frame), insets);
}
return insets;
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index df9e23e427f8..5f6d5e29570e 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -21,25 +21,37 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.DisplayMetrics;
/**
* Helper class for drawing round scroll bars on round Wear devices.
*/
class RoundScrollbarRenderer {
// The range of the scrollbar position represented as an angle in degrees.
- private static final int SCROLLBAR_ANGLE_RANGE = 90;
- private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16;
- private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6;
- private static final float WIDTH_PERCENTAGE = 0.02f;
- private static final int DEFAULT_THUMB_COLOR = 0xFFE8EAED;
+ private static final float SCROLLBAR_ANGLE_RANGE = 28.8f;
+ private static final float MAX_SCROLLBAR_ANGLE_SWIPE = 26.3f; // 90%
+ private static final float MIN_SCROLLBAR_ANGLE_SWIPE = 3.1f; // 10%
+ private static final float THUMB_WIDTH_DP = 4f;
+ private static final float OUTER_PADDING_DP = 2f;
+ private static final int DEFAULT_THUMB_COLOR = 0xFFFFFFFF;
private static final int DEFAULT_TRACK_COLOR = 0x4CFFFFFF;
+ // Rate at which the scrollbar will resize itself when the size of the view changes
+ private static final float RESIZING_RATE = 0.8f;
+ // Threshold at which the scrollbar will stop resizing smoothly and jump to the correct size
+ private static final int RESIZING_THRESHOLD_PX = 20;
+
private final Paint mThumbPaint = new Paint();
private final Paint mTrackPaint = new Paint();
private final RectF mRect = new RectF();
private final View mParent;
private final int mMaskThickness;
+ private float mPreviousMaxScroll = 0;
+ private float mMaxScrollDiff = 0;
+ private float mPreviousCurrentScroll = 0;
+ private float mCurrentScrollDiff = 0;
+
public RoundScrollbarRenderer(View parent) {
// Paints for the round scrollbar.
// Set up the thumb paint
@@ -61,19 +73,50 @@ class RoundScrollbarRenderer {
com.android.internal.R.dimen.circular_display_mask_thickness);
}
- public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) {
+ public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds, boolean drawToLeft) {
if (alpha == 0) {
return;
}
// Get information about the current scroll state of the parent view.
float maxScroll = mParent.computeVerticalScrollRange();
float scrollExtent = mParent.computeVerticalScrollExtent();
- if (scrollExtent <= 0 || maxScroll <= scrollExtent) {
+ float newScroll = mParent.computeVerticalScrollOffset();
+
+ if (scrollExtent <= 0) {
+ if (!mParent.canScrollVertically(1) && !mParent.canScrollVertically(-1)) {
+ return;
+ } else {
+ scrollExtent = 0;
+ }
+ } else if (maxScroll <= scrollExtent) {
return;
}
- float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset());
- float linearThumbLength = mParent.computeVerticalScrollExtent();
- float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE;
+
+ // Make changes to the VerticalScrollRange happen gradually
+ if (Math.abs(maxScroll - mPreviousMaxScroll) > RESIZING_THRESHOLD_PX
+ && mPreviousMaxScroll != 0) {
+ mMaxScrollDiff += maxScroll - mPreviousMaxScroll;
+ mCurrentScrollDiff += newScroll - mPreviousCurrentScroll;
+ }
+
+ mPreviousMaxScroll = maxScroll;
+ mPreviousCurrentScroll = newScroll;
+
+ if (Math.abs(mMaxScrollDiff) > RESIZING_THRESHOLD_PX
+ || Math.abs(mCurrentScrollDiff) > RESIZING_THRESHOLD_PX) {
+ mMaxScrollDiff *= RESIZING_RATE;
+ mCurrentScrollDiff *= RESIZING_RATE;
+
+ maxScroll -= mMaxScrollDiff;
+ newScroll -= mCurrentScrollDiff;
+ } else {
+ mMaxScrollDiff = 0;
+ mCurrentScrollDiff = 0;
+ }
+
+ float currentScroll = Math.max(0, newScroll);
+ float linearThumbLength = scrollExtent;
+ float thumbWidth = dpToPx(THUMB_WIDTH_DP);
mThumbPaint.setStrokeWidth(thumbWidth);
mTrackPaint.setStrokeWidth(thumbWidth);
@@ -85,9 +128,9 @@ class RoundScrollbarRenderer {
sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE);
// Normalize the start angle so that it falls on the track.
float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle))
- / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2;
- startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2,
- SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle);
+ / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2f;
+ startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2f,
+ SCROLLBAR_ANGLE_RANGE / 2f - sweepAngle);
// Draw the track and the thumb.
float inset = thumbWidth / 2 + mMaskThickness;
@@ -96,9 +139,26 @@ class RoundScrollbarRenderer {
bounds.top + inset,
bounds.right - inset,
bounds.bottom - inset);
- canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false,
- mTrackPaint);
- canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
+
+ if (drawToLeft) {
+ canvas.drawArc(mRect, 180 + SCROLLBAR_ANGLE_RANGE / 2f, -SCROLLBAR_ANGLE_RANGE, false,
+ mTrackPaint);
+ canvas.drawArc(mRect, 180 - startAngle, -sweepAngle, false, mThumbPaint);
+ } else {
+ canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2f, SCROLLBAR_ANGLE_RANGE, false,
+ mTrackPaint);
+ canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
+ }
+ }
+
+ void getRoundVerticalScrollBarBounds(Rect bounds) {
+ float padding = dpToPx(OUTER_PADDING_DP);
+ final int width = mParent.mRight - mParent.mLeft;
+ final int height = mParent.mBottom - mParent.mTop;
+ bounds.left = mParent.mScrollX + (int) padding;
+ bounds.top = mParent.mScrollY + (int) padding;
+ bounds.right = mParent.mScrollX + width - (int) padding;
+ bounds.bottom = mParent.mScrollY + height - (int) padding;
}
private static float clamp(float val, float min, float max) {
@@ -127,4 +187,9 @@ class RoundScrollbarRenderer {
mTrackPaint.setColor(trackColor);
}
}
+
+ private float dpToPx(float dp) {
+ return dp * ((float) mParent.getContext().getResources().getDisplayMetrics().densityDpi)
+ / DisplayMetrics.DENSITY_DEFAULT;
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 71e9052627b9..565ed61ce30d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8662,15 +8662,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Dispatches an {@link AccessibilityEvent} to the {@link View} first and then
- * to its children for adding their text content to the event. Note that the
- * event text is populated in a separate dispatch path since we add to the
+ * Dispatches an {@link AccessibilityEvent} to the {@link View} to add the text content of the
+ * view and its children.
+ * <p>
+ * <b>Note:</b> This method should only be used with event.setText().
+ * Avoid mutating other event state in this method. In general, put UI metadata in the node for
+ * services to easily query.
+ * <ul>
+ * <li> If you are modifying other event properties, you may be eliminating semantics
+ * accessibility services may want. Instead, send a separate event using
+ * {@link #sendAccessibilityEvent(int)} and override
+ * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * </li>
+ * <li>If you are checking for type {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
+ * to generate window/title announcements, you may be causing disruptive announcements
+ * (or making no announcements at all). Instead, follow the practices described in
+ * {@link View#announceForAccessibility(CharSequence)}. <b>Note:</b> this does not suggest
+ * calling announceForAccessibility(), but using the suggestions listed in its
+ * documentation.
+ * </li>
+ * <li>If you are making changes based on the state of accessibility, such as checking for
+ * an event type to trigger a UI update, while well-intentioned, you are creating brittle,
+ * less well-maintained code that works for some users but not others. Instead, leverage
+ * existing code for equitable experiences and less technical debt. See
+ * {@link AccessibilityManager#isEnabled()} for an example.
+ * </li>
+ * </ul>
+ * <p>
+ * Note that the event text is populated in a separate dispatch path
+ * ({@link #onPopulateAccessibilityEvent(AccessibilityEvent)}) since we add to the
* event not only the text of the source but also the text of all its descendants.
+ * <p>
* A typical implementation will call
- * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on the this view
+ * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on this view
* and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
- * on each child. Override this method if custom population of the event text
- * content is required.
+ * on each child or the first child that is visible. Override this method if custom population
+ * of the event text content is required.
+ *
* <p>
* If an {@link AccessibilityDelegate} has been specified via calling
* {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
@@ -8714,9 +8742,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
* giving a chance to this View to populate the accessibility event with its
- * text content. While this method is free to modify event
- * attributes other than text content, doing so should normally be performed in
- * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * text content.
+ * <p>
+ * <b>Note:</b> This method should only be used with event.setText().
+ * Avoid mutating other event state in this method. Instead, follow the practices described in
+ * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}. In general, put UI
+ * metadata in the node for services to easily query, than in transient events.
* <p>
* Example: Adding formatted date string to an accessibility event in addition
* to the text added by the super implementation:
@@ -11256,26 +11287,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Sets the id of a view before which this one is visited in accessibility traversal.
- * A screen-reader must visit the content of this view before the content of the one
- * it precedes. For example, if view B is set to be before view A, then a screen-reader
- * will traverse the entire content of B before traversing the entire content of A,
- * regardles of what traversal strategy it is using.
+ * Sets the id of a view that screen readers are requested to visit after this view.
+ *
* <p>
- * Views that do not have specified before/after relationships are traversed in order
- * determined by the screen-reader.
- * </p>
+ *
+ * For example, if view B should be visited before view A, with
+ * B.setAccessibilityTraversalBefore(A), this requests that screen readers visit and traverse
+ * view B before visiting view A.
+ *
* <p>
- * Setting that this view is before a view that is not important for accessibility
- * or if this view is not important for accessibility will have no effect as the
- * screen-reader is not aware of unimportant views.
- * </p>
+ * <b>Note:</b> Views are visited in the order determined by the screen reader. Avoid
+ * explicitly manipulating focus order, as this may result in inconsistent user
+ * experiences between apps. Instead, use other semantics, such as restructuring the view
+ * hierarchy layout, to communicate order.
+ *
+ * <p>
+ * Setting this view to be after a view that is not important for accessibility,
+ * or if this view is not important for accessibility, means this method will have no effect if
+ * the service is not aware of unimportant views.
+ *
+ * <p>
+ * To avoid a risk of loops, set clear relationships between views. For example, if focus order
+ * should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
+ * A.setAccessibilityTraversalAfter(B).
*
* @param beforeId The id of a view this one precedes in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalBefore
*
* @see #setImportantForAccessibility(int)
+ * @see #setAccessibilityTraversalAfter(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalBefore(@IdRes int beforeId) {
@@ -11302,26 +11343,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Sets the id of a view after which this one is visited in accessibility traversal.
- * A screen-reader must visit the content of the other view before the content of this
- * one. For example, if view B is set to be after view A, then a screen-reader
- * will traverse the entire content of A before traversing the entire content of B,
- * regardles of what traversal strategy it is using.
+ * Sets the id of a view that screen readers are requested to visit before this view.
+ *
* <p>
- * Views that do not have specified before/after relationships are traversed in order
- * determined by the screen-reader.
- * </p>
+ * For example, if view B should be visited after A, with B.setAccessibilityTraversalAfter(A),
+ * then this requests that screen readers visit and traverse view A before visiting view B.
+ *
* <p>
- * Setting that this view is after a view that is not important for accessibility
- * or if this view is not important for accessibility will have no effect as the
- * screen-reader is not aware of unimportant views.
- * </p>
+ * <b>Note:</b> Views are visited in the order determined by the screen reader. Avoid
+ * explicitly manipulating focus order, as this may result in inconsistent user
+ * experiences between apps. Instead, use other semantics, such as restructuring the view
+ * hierarchy layout, to communicate order.
*
- * @param afterId The id of a view this one succedees in accessibility traversal.
+ * <p>
+ * Setting this view to be after a view that is not important for accessibility,
+ * or if this view is not important for accessibility, means this method will have no effect if
+ * the service is not aware of unimportant views.
+ *
+ * <p>
+ * To avoid a risk of loops, set clear relationships between views. For example, if focus order
+ * should be B -> A, and B.setAccessibilityTraversalBefore(A), then also call
+ * A.setAccessibilityTraversalAfter(B).
+ *
+ * @param afterId The id of a view this one succeeds in accessibility traversal.
*
* @attr ref android.R.styleable#View_accessibilityTraversalAfter
*
* @see #setImportantForAccessibility(int)
+ * @see #setAccessibilityTraversalBefore(int)
*/
@RemotableViewMethod
public void setAccessibilityTraversalAfter(@IdRes int afterId) {
@@ -14619,19 +14668,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* to the view's content description or text, or to the content descriptions
* or text of the view's children (where applicable).
* <p>
- * For example, in a login screen with a TextView that displays an "incorrect
- * password" notification, that view should be marked as a live region with
- * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+ * To indicate that the user should be notified of changes, use
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. Announcements from this region are queued and
+ * do not disrupt ongoing speech.
+ * <p>
+ * For example, selecting an option in a dropdown menu may update a panel below with the updated
+ * content. This panel may be marked as a live region with
+ * {@link #ACCESSIBILITY_LIVE_REGION_POLITE} to notify users of the change.
+ * <p>
+ * For notifying users about errors, such as in a login screen with text that displays an
+ * "incorrect password" notification, that view should send an AccessibilityEvent of type
+ * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
+ * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
+ * error-setting methods that support accessibility automatically. For example, instead of
+ * explicitly sending this event when using a TextView, use
+ * {@link android.widget.TextView#setError(CharSequence)}.
* <p>
* To disable change notifications for this view, use
* {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
* mode for most views.
* <p>
- * To indicate that the user should be notified of changes, use
- * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
- * <p>
* If the view's changes should interrupt ongoing speech and notify the user
- * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
+ * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. This may result in disruptive
+ * announcements from an accessibility service, so it should generally be used only to convey
+ * information that is time-sensitive or critical for use of the application. Examples may
+ * include an incoming call or an emergency alert.
* <p>
* <b>Note:</b> Use {@link androidx.core.view.ViewCompat#setAccessibilityLiveRegion(View, int)}
* for backwards-compatibility. </aside>
@@ -21190,21 +21251,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mRoundScrollbarRenderer == null) {
getStraightVerticalScrollBarBounds(bounds, touchBounds);
} else {
- getRoundVerticalScrollBarBounds(bounds != null ? bounds : touchBounds);
+ mRoundScrollbarRenderer.getRoundVerticalScrollBarBounds(
+ bounds != null ? bounds : touchBounds);
}
}
- private void getRoundVerticalScrollBarBounds(Rect bounds) {
- final int width = mRight - mLeft;
- final int height = mBottom - mTop;
- // Do not take padding into account as we always want the scrollbars
- // to hug the screen for round wearable devices.
- bounds.left = mScrollX;
- bounds.top = mScrollY;
- bounds.right = bounds.left + width;
- bounds.bottom = mScrollY + height;
- }
-
private void getStraightVerticalScrollBarBounds(@Nullable Rect drawBounds,
@Nullable Rect touchBounds) {
final Rect bounds = drawBounds != null ? drawBounds : touchBounds;
@@ -21314,8 +21365,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (drawVerticalScrollBar) {
final Rect bounds = cache.mScrollBarBounds;
getVerticalScrollBarBounds(bounds, null);
+ boolean shouldDrawScrollbarAtLeft =
+ (mVerticalScrollbarPosition == SCROLLBAR_POSITION_LEFT)
+ || (mVerticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT
+ && isLayoutRtl());
+
mRoundScrollbarRenderer.drawRoundScrollbars(
- canvas, (float) cache.scrollBar.getAlpha() / 255f, bounds);
+ canvas, (float) cache.scrollBar.getAlpha() / 255f, bounds,
+ shouldDrawScrollbarAtLeft);
if (invalidate) {
invalidate();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5eaf089b4caa..1d90270bf73f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3859,7 +3859,15 @@ public final class ViewRootImpl implements ViewParent,
mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
mWmsRequestSyncGroup = new SurfaceSyncGroup("wmsSync-" + mTag, t -> {
mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
- reportDrawFinished(t, seqId);
+ // See b/286355097. If the current process is not system, then invoking finishDraw on
+ // any thread is fine since once it calls into system process, finishDrawing will run
+ // on a different thread. However, when the current process is system, the finishDraw in
+ // system server will be run on the current thread, which could result in a deadlock.
+ if (mWindowSession instanceof Binder) {
+ reportDrawFinished(t, seqId);
+ } else {
+ mHandler.postAtFrontOfQueue(() -> reportDrawFinished(t, seqId));
+ }
});
if (DEBUG_BLAST) {
Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
@@ -10903,12 +10911,16 @@ public final class ViewRootImpl implements ViewParent,
}
}
- public void attachAccessibilityOverlayToWindow(SurfaceControl sc) {
+ public void attachAccessibilityOverlayToWindow(
+ SurfaceControl sc,
+ int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null) {
viewRootImpl
.getAccessibilityInteractionController()
- .attachAccessibilityOverlayToWindowClientThread(sc);
+ .attachAccessibilityOverlayToWindowClientThread(
+ sc, interactionId, callback);
}
}
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 4acaea849586..57a41619ff8d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -84,13 +84,7 @@ public final class WindowInsets {
@Nullable private final PrivacyIndicatorBounds mPrivacyIndicatorBounds;
@Nullable private final DisplayShape mDisplayShape;
- /**
- * In multi-window we force show the navigation bar. Because we don't want that the surface size
- * changes in this mode, we instead have a flag whether the navigation bar size should always
- * be consumed, so the app is treated like there is no virtual navigation bar at all.
- */
- private final boolean mAlwaysConsumeSystemBars;
-
+ private final @InsetsType int mForceConsumingTypes;
private final @InsetsType int mSuppressScrimTypes;
private final boolean mSystemWindowInsetsConsumed;
private final boolean mStableInsetsConsumed;
@@ -117,7 +111,7 @@ public final class WindowInsets {
static {
CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null),
- createCompatVisibilityMap(createCompatTypeMap(null)), false, false, 0, null,
+ createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, 0, null,
null, null, null, systemBars(), false);
}
@@ -137,7 +131,8 @@ public final class WindowInsets {
@Nullable Insets[] typeMaxInsetsMap,
boolean[] typeVisibilityMap,
boolean isRound,
- boolean alwaysConsumeSystemBars, @InsetsType int suppressScrimTypes,
+ @InsetsType int forceConsumingTypes,
+ @InsetsType int suppressScrimTypes,
DisplayCutout displayCutout,
RoundedCorners roundedCorners,
PrivacyIndicatorBounds privacyIndicatorBounds,
@@ -155,7 +150,7 @@ public final class WindowInsets {
mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
- mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ mForceConsumingTypes = forceConsumingTypes;
mSuppressScrimTypes = suppressScrimTypes;
mCompatInsetsTypes = compatInsetsTypes;
mCompatIgnoreVisibility = compatIgnoreVisibility;
@@ -178,7 +173,7 @@ public final class WindowInsets {
this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
- src.mAlwaysConsumeSystemBars, src.mSuppressScrimTypes,
+ src.mForceConsumingTypes, src.mSuppressScrimTypes,
displayCutoutCopyConstructorArgument(src),
src.mRoundedCorners,
src.mPrivacyIndicatorBounds,
@@ -235,7 +230,7 @@ public final class WindowInsets {
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, 0,
+ this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, 0,
null, null, null, null, systemBars(), false /* compatIgnoreVisibility */);
}
@@ -556,7 +551,7 @@ public final class WindowInsets {
return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape,
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -607,7 +602,7 @@ public final class WindowInsets {
public WindowInsets consumeSystemWindowInsets() {
return new WindowInsets(null, null,
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
// If the system window insets types contain displayCutout, we should also consume
// it.
(mCompatInsetsTypes & displayCutout()) != 0
@@ -895,8 +890,8 @@ public final class WindowInsets {
/**
* @hide
*/
- public boolean shouldAlwaysConsumeSystemBars() {
- return mAlwaysConsumeSystemBars;
+ public @InsetsType int getForceConsumingTypes() {
+ return mForceConsumingTypes;
}
/**
@@ -930,6 +925,8 @@ public final class WindowInsets {
result.append("\n ");
result.append(mDisplayShape != null ? "displayShape=" + mDisplayShape : "");
result.append("\n ");
+ result.append("forceConsumingTypes=" + Type.toString(mForceConsumingTypes));
+ result.append("\n ");
result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes));
result.append("\n ");
result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes));
@@ -1027,7 +1024,7 @@ public final class WindowInsets {
? null
: insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
mDisplayCutoutConsumed
? null
: mDisplayCutout == null
@@ -1050,7 +1047,7 @@ public final class WindowInsets {
WindowInsets that = (WindowInsets) o;
return mIsRound == that.mIsRound
- && mAlwaysConsumeSystemBars == that.mAlwaysConsumeSystemBars
+ && mForceConsumingTypes == that.mForceConsumingTypes
&& mSuppressScrimTypes == that.mSuppressScrimTypes
&& mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
&& mStableInsetsConsumed == that.mStableInsetsConsumed
@@ -1068,7 +1065,7 @@ public final class WindowInsets {
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
- mAlwaysConsumeSystemBars, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
+ mForceConsumingTypes, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds,
mDisplayShape);
}
@@ -1134,7 +1131,7 @@ public final class WindowInsets {
private DisplayShape mDisplayShape = DisplayShape.NONE;
private boolean mIsRound;
- private boolean mAlwaysConsumeSystemBars;
+ private @InsetsType int mForceConsumingTypes;
private @InsetsType int mSuppressScrimTypes;
private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds();
@@ -1162,7 +1159,7 @@ public final class WindowInsets {
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
mRoundedCorners = insets.mRoundedCorners;
mIsRound = insets.mIsRound;
- mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
+ mForceConsumingTypes = insets.mForceConsumingTypes;
mSuppressScrimTypes = insets.mSuppressScrimTypes;
mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds;
mDisplayShape = insets.mDisplayShape;
@@ -1433,7 +1430,15 @@ public final class WindowInsets {
/** @hide */
@NonNull
public Builder setAlwaysConsumeSystemBars(boolean alwaysConsumeSystemBars) {
- mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ // TODO (b/277891341): Remove this and related usages. This has been replaced by
+ // #setForceConsumingTypes.
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ public Builder setForceConsumingTypes(@InsetsType int forceConsumingTypes) {
+ mForceConsumingTypes = forceConsumingTypes;
return this;
}
@@ -1453,7 +1458,7 @@ public final class WindowInsets {
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes, mDisplayCutout,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes, mDisplayCutout,
mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(),
false /* compatIgnoreVisibility */);
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index d9fcfb578232..f14ec37ca402 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -62,6 +62,7 @@ import java.util.List;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntConsumer;
/**
* This class is a singleton that performs accessibility interaction
@@ -158,11 +159,15 @@ public final class AccessibilityInteractionClient
private final SparseArray<Pair<Executor, AccessibilityService.TakeScreenshotCallback>>
mTakeScreenshotOfWindowCallbacks = new SparseArray<>();
+ // SparseArray of interaction ID -> overlay executor+callback.
+ private final SparseArray<Pair<Executor, IntConsumer>> mAttachAccessibilityOverlayCallbacks =
+ new SparseArray<>();
private Message mSameThreadMessage;
private int mInteractionIdWaitingForPrefetchResult = -1;
private int mConnectionIdWaitingForPrefetchResult;
private String[] mPackageNamesForNextPrefetchResult;
+ private Handler mMainHandler = new Handler(Looper.getMainLooper());
/**
* @return The client for the current thread.
@@ -830,7 +835,7 @@ public final class AccessibilityInteractionClient
interactionId));
connection.takeScreenshotOfWindow(accessibilityWindowId, interactionId,
listener, this);
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ mMainHandler.postDelayed(() -> {
synchronized (mInstanceLock) {
// Notify failure if we still haven't sent a response after timeout.
if (mTakeScreenshotOfWindowCallbacks.contains(interactionId)) {
@@ -1621,7 +1626,11 @@ public final class AccessibilityInteractionClient
private void logTraceClient(
IAccessibilityServiceConnection connection, String method, String params) {
- logTrace(connection, method, params, Binder.getCallingUid(),
+ logTrace(
+ connection,
+ method,
+ params,
+ Binder.getCallingUid(),
Arrays.asList(Thread.currentThread().getStackTrace()),
new HashSet<String>(Arrays.asList("getStackTrace", "logTraceClient")),
FLAGS_ACCESSIBILITY_INTERACTION_CLIENT);
@@ -1629,18 +1638,105 @@ public final class AccessibilityInteractionClient
/** Attaches an accessibility overlay to the specified window. */
public void attachAccessibilityOverlayToWindow(
- int connectionId, int accessibilityWindowId, SurfaceControl sc) {
+ int connectionId,
+ int accessibilityWindowId,
+ SurfaceControl sc,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
+ synchronized (mInstanceLock) {
+ try {
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection == null) {
+ executor.execute(
+ () ->
+ callback.accept(
+ AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR));
+ return;
+ }
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ mAttachAccessibilityOverlayCallbacks.put(
+ interactionId, Pair.create(executor, callback));
+ connection.attachAccessibilityOverlayToWindow(
+ interactionId, accessibilityWindowId, sc, this);
+ mMainHandler.postDelayed(
+ () -> {
+ synchronized (mInstanceLock) {
+ // Notify failure if we still haven't sent a response after timeout.
+ if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
+ sendAttachOverlayResult(
+ AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR,
+ interactionId);
+ }
+ }
+ },
+ TIMEOUT_INTERACTION_MILLIS);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /** Attaches an accessibility overlay to the specified display. */
+ public void attachAccessibilityOverlayToDisplay(
+ int connectionId,
+ int displayId,
+ SurfaceControl sc,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
synchronized (mInstanceLock) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection == null) {
- Log.e(LOG_TAG, "Error while getting service connection.");
+ executor.execute(
+ () ->
+ callback.accept(
+ AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR));
return;
}
- connection.attachAccessibilityOverlayToWindow(accessibilityWindowId, sc);
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ mAttachAccessibilityOverlayCallbacks.put(
+ interactionId, Pair.create(executor, callback));
+ connection.attachAccessibilityOverlayToDisplay(interactionId, displayId, sc, this);
+ mMainHandler.postDelayed(
+ () -> {
+ // Notify failure if we still haven't sent a response after timeout.
+ if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
+ sendAttachOverlayResult(
+ AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR,
+ interactionId);
+ }
+ },
+ TIMEOUT_INTERACTION_MILLIS);
} catch (RemoteException re) {
re.rethrowFromSystemServer();
}
}
}
+
+ /**
+ * Sends a result code for an attach window overlay request to the requesting client.
+ *
+ * @param result The result code from {@link AccessibilityService.OverlayResult}.
+ * @param interactionId The interaction id of the request.
+ */
+ @Override
+ public void sendAttachOverlayResult(
+ @AccessibilityService.AttachOverlayResult int result, int interactionId) {
+ synchronized (mInstanceLock) {
+ if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
+ final Pair<Executor, IntConsumer> pair =
+ mAttachAccessibilityOverlayCallbacks.get(interactionId);
+ if (pair == null) {
+ return;
+ }
+ final Executor executor = pair.first;
+ final IntConsumer callback = pair.second;
+ if (executor == null || callback == null) {
+ return;
+ }
+ executor.execute(() -> callback.accept(result));
+ mAttachAccessibilityOverlayCallbacks.remove(interactionId);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index fa0052cf664a..09b2f9c1ee15 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -99,6 +99,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
/** @hide */
public static final int UNDEFINED_CONNECTION_ID = -1;
/** @hide */
+ @TestApi
public static final int UNDEFINED_WINDOW_ID = -1;
/** @hide */
public static final int ANY_WINDOW_ID = -2;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 0eeba7ccf46a..a43acf9147f3 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -66,5 +66,6 @@ oneway interface IAccessibilityInteractionConnection {
void takeScreenshotOfWindow(int interactionId,
in ScreenCapture.ScreenCaptureListener listener,
IAccessibilityInteractionConnectionCallback callback);
- void attachAccessibilityOverlayToWindow(in SurfaceControl sc);
+
+ void attachAccessibilityOverlayToWindow(in SurfaceControl sc, int interactionId, in IAccessibilityInteractionConnectionCallback callback);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index 456bf5893ea0..fe595193047c 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -68,4 +68,9 @@ oneway interface IAccessibilityInteractionConnectionCallback {
* Sends an error code for a window screenshot request to the requesting client.
*/
void sendTakeScreenshotOfWindowError(int errorCode, int interactionId);
+
+ /**
+ * Sends an result code for an attach overlay request to the requesting client.
+ */
+ void sendAttachOverlayResult(int result, int interactionId);
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index f31a43f5924f..8ba8b8cca5ed 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -30,6 +30,7 @@ import android.content.res.Resources.Theme;
import android.content.res.XmlResourceParser;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.TimeUtils;
import android.util.Xml;
import android.view.InflateException;
@@ -156,6 +157,18 @@ public class AnimationUtils {
}
/**
+ * The expected presentation time of a frame in the {@link SystemClock#uptimeMillis()}.
+ * Developers should prefer using this method over {@link #currentAnimationTimeMillis()}
+ * because it offers a more accurate time for the calculating animation progress.
+ *
+ * @return the expected presentation time of a frame in the
+ * {@link SystemClock#uptimeMillis()} time base.
+ */
+ public static long getExpectedPresentationTimeMillis() {
+ return getExpectedPresentationTimeNanos() / TimeUtils.NANOS_PER_MS;
+ }
+
+ /**
* Loads an {@link Animation} object from a resource
*
* @param context Application context used to access resources
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index 622b0e208812..37c6f5bf3425 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -4,3 +4,6 @@ simranjit@google.com
haoranzhang@google.com
skxu@google.com
yunicorn@google.com
+
+# Bug component: 543785 = per-file *Augmented*
+per-file *Augmented* = wangqi@google.com
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 92380ed7a7bc..6340388b5d5b 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -282,7 +282,11 @@ public interface InputMethod {
* Flag for {@link #showSoftInput}: this show has been forced to
* happen by the user. If set, the input method should remain visible
* until deliberated dismissed by the user in its UI.
+ *
+ * @deprecated {@link InputMethodManager#SHOW_FORCED} is deprecated and
+ * should no longer be used by apps. IMEs likewise should no longer react to this flag.
*/
+ @Deprecated
public static final int SHOW_FORCED = 0x00002;
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3f308e6fccee..fb94d49276cb 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2018,7 +2018,7 @@ public final class InputMethodManager {
*
* @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead
* to the soft input remaining visible even when the calling application is closed. The
- * use of this flag can make the soft input remains visible globally. Starting in
+ * use of this flag can make the soft input remain visible globally. Starting in
* {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the
* caller is currently focused.
*/
diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS
index a205be2f39d0..224ed71f3401 100644
--- a/core/java/android/view/textclassifier/OWNERS
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -1,8 +1,3 @@
# Bug component: 709498
-mns@google.com
-toki@google.com
-augale@google.com
-joannechung@google.com
-tonymak@google.com
-licha@google.com
+wangqi@google.com
diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS
index b772ad3f7cab..977bda16c2d0 100644
--- a/core/java/android/view/translation/OWNERS
+++ b/core/java/android/view/translation/OWNERS
@@ -1,7 +1,3 @@
# Bug component: 994311
-augale@google.com
-joannechung@google.com
-markpun@google.com
-lpeter@google.com
-tymtsai@google.com
+wangqi@google.com
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 95ba36ab80f9..cc50f793abc8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -156,7 +156,6 @@ import android.text.util.Linkify;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseIntArray;
@@ -831,11 +830,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
private int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
- // The auto option for LINE_BREAK_WORD_STYLE_PHRASE may not be applied in recycled view due to
- // one-way flag flipping. This is a tentative limitation during experiment and will not have the
- // issue once this is finalized to LINE_BREAK_WORD_STYLE_PHRASE_AUTO option.
- private boolean mUserSpeficiedLineBreakwordStyle = false;
-
// This is used to reflect the current user preference for changing font weight and making text
// more bold.
private int mFontWeightAdjustment;
@@ -1546,9 +1540,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
break;
case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
- if (a.hasValue(attr)) {
- mUserSpeficiedLineBreakwordStyle = true;
- }
mLineBreakWordStyle = a.getInt(attr,
LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE);
break;
@@ -4350,7 +4341,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
break;
case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle:
attributes.mHasLineBreakWordStyle = true;
- mUserSpeficiedLineBreakwordStyle = true;
attributes.mLineBreakWordStyle =
appearance.getInt(attr, attributes.mLineBreakWordStyle);
break;
@@ -5086,7 +5076,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param lineBreakWordStyle The line-break word style for the text.
*/
public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
- mUserSpeficiedLineBreakwordStyle = true;
if (mLineBreakWordStyle != lineBreakWordStyle) {
mLineBreakWordStyle = lineBreakWordStyle;
if (mLayout != null) {
@@ -5122,12 +5111,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @see PrecomputedText
*/
public @NonNull PrecomputedText.Params getTextMetricsParams() {
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
return new PrecomputedText.Params(new TextPaint(mTextPaint),
- LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle,
- autoPhraseBreaking),
+ LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle),
getTextDirectionHeuristic(),
mBreakStrategy, mHyphenationFrequency);
}
@@ -5145,9 +5130,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mBreakStrategy = params.getBreakStrategy();
mHyphenationFrequency = params.getHyphenationFrequency();
LineBreakConfig lineBreakConfig = params.getLineBreakConfig();
- mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
- mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
- mUserSpeficiedLineBreakwordStyle = true;
+ mLineBreakStyle = LineBreakConfig.getResolvedLineBreakStyle(lineBreakConfig);
+ mLineBreakWordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(lineBreakConfig);
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -7077,13 +7061,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
mHyphenationFrequency, LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
switch (checkResult) {
case PrecomputedText.Params.UNUSABLE:
throw new IllegalArgumentException(
@@ -10640,9 +10621,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
// TODO: code duplication with makeSingleLayout()
if (mHintLayout == null) {
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
@@ -10655,7 +10633,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setJustificationMode(mJustificationMode)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
if (shouldEllipsize) {
builder.setEllipsize(mEllipsize)
.setEllipsizedWidth(ellipsisWidth);
@@ -10704,7 +10682,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean useSaved) {
Layout result = null;
if (useDynamicLayout()) {
- final boolean autoPhraseBreaking = isAutoPhraseBreakingEnabled();
final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
wantWidth)
.setDisplayText(mTransformed)
@@ -10717,7 +10694,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking))
+ mLineBreakStyle, mLineBreakWordStyle))
.setEllipsize(getKeyListener() == null ? effectiveEllipsize : null)
.setEllipsizedWidth(ellipsisWidth);
result = builder.build();
@@ -10762,7 +10739,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
if (result == null) {
- final boolean autoPhraseBreaking = isAutoPhraseBreakingEnabled();
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
@@ -10775,7 +10751,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setJustificationMode(mJustificationMode)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
if (shouldEllipsize) {
builder.setEllipsize(effectiveEllipsize)
.setEllipsizedWidth(ellipsisWidth);
@@ -10785,11 +10761,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return result;
}
- private boolean isAutoPhraseBreakingEnabled() {
- return !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
- }
-
@UnsupportedAppUsage
private boolean compressText(float width) {
if (isHardwareAccelerated()) return false;
@@ -11138,9 +11109,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
text, 0, text.length(), mTempTextPaint, Math.round(availableSpace.right));
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
layoutBuilder.setAlignment(getLayoutAlignment())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
@@ -11151,7 +11119,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setTextDirection(getTextDirectionHeuristic())
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
final StaticLayout layout = layoutBuilder.build();
diff --git a/core/java/android/widget/inline/OWNERS b/core/java/android/widget/inline/OWNERS
index 9a30e826a24f..73651daa562c 100644
--- a/core/java/android/widget/inline/OWNERS
+++ b/core/java/android/widget/inline/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 351486
+# Bug component: 1195602
-include /core/java/android/view/autofill/OWNERS
+wangqi@google.com
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 954f68633e57..2858f0a1a725 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -145,13 +145,13 @@ public final class WindowMetricsController {
for (int i = 0; i < possibleDisplayInfos.size(); i++) {
currentDisplayInfo = possibleDisplayInfos.get(i);
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
+ // Calculate max bounds for natural rotation and state.
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
+ currentDisplayInfo.getNaturalHeight());
- // Calculate insets for the rotated max bounds.
+ // Calculate insets for the natural max bounds.
final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
+ // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets
// will not be set.
windowInsets = getWindowInsetsFromServerForDisplay(
currentDisplayInfo.displayId, null /* token */,
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index f2ae973500af..611da3cec5c6 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -34,6 +34,7 @@ import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
@@ -52,6 +53,8 @@ import android.view.WindowManagerImpl;
@UiContext
public abstract class WindowProviderService extends Service implements WindowProvider {
+ private static final String TAG = WindowProviderService.class.getSimpleName();
+
private final Bundle mOptions;
private final WindowTokenClient mWindowToken = new WindowTokenClient();
private final WindowContextController mController = new WindowContextController(mWindowToken);
@@ -194,8 +197,16 @@ public abstract class WindowProviderService extends Service implements WindowPro
public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo);
- final Display display = context.getSystemService(DisplayManager.class)
- .getDisplay(getInitialDisplayId());
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ final int initialDisplayId = getInitialDisplayId();
+ Display display = displayManager.getDisplay(initialDisplayId);
+ // Fallback to use the default display if the initial display to start WindowProviderService
+ // is detached.
+ if (display == null) {
+ Log.e(TAG, "Display with id " + initialDisplayId + " not found, falling back to "
+ + "DEFAULT_DISPLAY");
+ display = displayManager.getDisplay(DEFAULT_DISPLAY);
+ }
return context.createTokenContext(mWindowToken, display);
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7452daa4908c..65b59790e327 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -56,6 +56,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.widget.Button;
@@ -124,16 +125,19 @@ public class IntentForwarderActivity extends Activity {
String className = intentReceived.getComponent().getClassName();
final int targetUserId;
final String userMessage;
+ final UserInfo managedProfile;
if (className.equals(FORWARD_INTENT_TO_PARENT)) {
userMessage = getForwardToPersonalMessage();
targetUserId = getProfileParent();
+ managedProfile = null;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
.setSubtype(MetricsEvent.PARENT_PROFILE));
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
userMessage = getForwardToWorkMessage();
- targetUserId = getManagedProfile();
+ managedProfile = getManagedProfile();
+ targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
@@ -142,6 +146,7 @@ public class IntentForwarderActivity extends Activity {
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
userMessage = null;
targetUserId = UserHandle.USER_NULL;
+ managedProfile = null;
}
if (targetUserId == UserHandle.USER_NULL) {
// This covers the case where there is no parent / managed profile.
@@ -185,27 +190,49 @@ public class IntentForwarderActivity extends Activity {
finish();
// When switching to the work profile, ask the user for consent before launching
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- maybeShowUserConsentMiniResolver(result, newIntent, targetUserId);
+ maybeShowUserConsentMiniResolver(result, newIntent, managedProfile);
}
}, getApplicationContext().getMainExecutor());
}
private void maybeShowUserConsentMiniResolver(
- ResolveInfo target, Intent launchIntent, int targetUserId) {
+ ResolveInfo target, Intent launchIntent, UserInfo managedProfile) {
if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
finish();
return;
}
- if (launchIntent.getBooleanExtra(EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
- && getCallingPackage() != null
- && PERMISSION_GRANTED == getPackageManager().checkPermission(
- INTERACT_ACROSS_USERS, getCallingPackage())) {
+ int targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
+ String callingPackage = getCallingPackage();
+ boolean privilegedCallerAskedToSkipUserConsent =
+ launchIntent.getBooleanExtra(
+ EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
+ && callingPackage != null
+ && PERMISSION_GRANTED == getPackageManager().checkPermission(
+ INTERACT_ACROSS_USERS, callingPackage);
+
+ DevicePolicyManager devicePolicyManager =
+ getSystemService(DevicePolicyManager.class);
+ ComponentName profileOwnerName = devicePolicyManager.getProfileOwnerAsUser(targetUserId);
+ boolean intentToLaunchProfileOwner = profileOwnerName != null
+ && profileOwnerName.getPackageName().equals(target.getComponentInfo().packageName);
+
+ if (privilegedCallerAskedToSkipUserConsent || intentToLaunchProfileOwner) {
+ Log.i("IntentForwarderActivity", String.format(
+ "Skipping user consent for redirection into the managed profile for intent [%s]"
+ + ", privilegedCallerAskedToSkipUserConsent=[%s]"
+ + ", intentToLaunchProfileOwner=[%s]",
+ launchIntent, privilegedCallerAskedToSkipUserConsent,
+ intentToLaunchProfileOwner));
startActivityAsCaller(launchIntent, targetUserId);
finish();
return;
}
+ Log.i("IntentForwarderActivity", String.format(
+ "Showing user consent for redirection into the managed profile for intent [%s] and "
+ + " calling package [%s]",
+ launchIntent, callingPackage));
int layoutId = R.layout.miniresolver;
setContentView(layoutId);
@@ -245,8 +272,7 @@ public class IntentForwarderActivity extends Activity {
View telephonyInfo = findViewById(R.id.miniresolver_info_section);
- DevicePolicyManager devicePolicyManager =
- getSystemService(DevicePolicyManager.class);
+
// Additional information section is work telephony specific. Therefore, it is only shown
// for telephony related intents, when all sim subscriptions are in the work profile.
if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent))
@@ -507,20 +533,18 @@ public class IntentForwarderActivity extends Activity {
}
/**
- * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
- * no managed profile.
+ * Returns the managed profile for this device or null if there is no managed profile.
*
- * TODO: Remove the assumption that there is only one managed profile
- * on the device.
+ * TODO: Remove the assumption that there is only one managed profile on the device.
*/
- private int getManagedProfile() {
+ @Nullable private UserInfo getManagedProfile() {
List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
for (UserInfo userInfo : relatedUsers) {
- if (userInfo.isManagedProfile()) return userInfo.id;
+ if (userInfo.isManagedProfile()) return userInfo;
}
Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
+ " has been called, but there is no managed profile");
- return UserHandle.USER_NULL;
+ return null;
}
/**
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 76e0e34ef7d3..d78689e9955b 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -22,15 +22,23 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Helper class for monitoring the state of packages: adding, removing,
@@ -41,8 +49,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
final IntentFilter mPackageFilt;
final IntentFilter mNonDataFilt;
- final IntentFilter mExternalFilt;
-
+
Context mRegisteredContext;
Handler mRegisteredHandler;
String[] mDisappearingPackages;
@@ -55,15 +62,16 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
String[] mTempArray = new String[1];
+ PackageMonitorCallback mPackageMonitorCallback;
+
@UnsupportedAppUsage
public PackageMonitor() {
final boolean isCore = UserHandle.isCore(android.os.Process.myUid());
mPackageFilt = new IntentFilter();
- mPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
- mPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
- mPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ // Settings app sends the broadcast
mPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ // AMS sends the broadcast
mPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
mPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
mPackageFilt.addDataScheme("package");
@@ -72,20 +80,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
mNonDataFilt = new IntentFilter();
- mNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+ // UserController sends the broadcast
mNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
- mNonDataFilt.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
- mNonDataFilt.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
if (isCore) {
mNonDataFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
}
-
- mExternalFilt = new IntentFilter();
- mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- if (isCore) {
- mExternalFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- }
}
@UnsupportedAppUsage
@@ -101,7 +100,16 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
public void register(Context context, UserHandle user,
- boolean externalStorage, Handler handler) {
+ boolean externalStorage, Handler handler) {
+ // Remove until all using code are updated to new method.
+ register(context, user, handler);
+ }
+
+
+ /**
+ * Register for notifications of package changes such as install, removal and other events.
+ */
+ public void register(Context context, UserHandle user, Handler handler) {
if (mRegisteredContext != null) {
throw new IllegalStateException("Already registered");
}
@@ -110,15 +118,17 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
if (user != null) {
context.registerReceiverAsUser(this, user, mPackageFilt, null, mRegisteredHandler);
context.registerReceiverAsUser(this, user, mNonDataFilt, null, mRegisteredHandler);
- if (externalStorage) {
- context.registerReceiverAsUser(this, user, mExternalFilt, null,
- mRegisteredHandler);
- }
} else {
context.registerReceiver(this, mPackageFilt, null, mRegisteredHandler);
context.registerReceiver(this, mNonDataFilt, null, mRegisteredHandler);
- if (externalStorage) {
- context.registerReceiver(this, mExternalFilt, null, mRegisteredHandler);
+ }
+ if (mPackageMonitorCallback == null) {
+ PackageManager pm = mRegisteredContext.getPackageManager();
+ if (pm != null) {
+ mPackageMonitorCallback = new PackageMonitorCallback(this,
+ new HandlerExecutor(mRegisteredHandler));
+ int userId = user != null ? user.getIdentifier() : mRegisteredContext.getUserId();
+ pm.registerPackageMonitorCallback(mPackageMonitorCallback, userId);
}
}
}
@@ -133,9 +143,15 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
throw new IllegalStateException("Not registered");
}
mRegisteredContext.unregisterReceiver(this);
+
+ PackageManager pm = mRegisteredContext.getPackageManager();
+ if (pm != null && mPackageMonitorCallback != null) {
+ pm.unregisterPackageMonitorCallback(mPackageMonitorCallback);
+ }
+ mPackageMonitorCallback = null;
mRegisteredContext = null;
}
-
+
public void onBeginPackageChanges() {
}
@@ -327,9 +343,18 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
return pkg;
}
-
+
@Override
public void onReceive(Context context, Intent intent) {
+ doHandlePackageEvent(intent);
+ }
+
+ /**
+ * Handle the package related event
+ * @param intent the intent that contains package related event information
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void doHandlePackageEvent(Intent intent) {
mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_NULL);
if (mChangeUserId == UserHandle.USER_NULL) {
@@ -337,11 +362,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
return;
}
onBeginPackageChanges();
-
+
mDisappearingPackages = mAppearingPackages = null;
mSomePackagesChanged = false;
mModifiedComponents = null;
-
+
String action = intent.getAction();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
String pkg = getPackageName(intent);
@@ -465,4 +490,30 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
onFinishPackageChanges();
mChangeUserId = UserHandle.USER_NULL;
}
+
+ private static final class PackageMonitorCallback extends IRemoteCallback.Stub {
+
+ private final PackageMonitor mPackageMonitor;
+ private final Executor mExecutor;
+
+ PackageMonitorCallback(PackageMonitor monitor, Executor executor) {
+ mPackageMonitor = monitor;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ onHandlePackageMonitorCallback(data);
+ }
+
+ private void onHandlePackageMonitorCallback(Bundle bundle) {
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ if (intent == null) {
+ Log.w(TAG, "No intent is set for PackageMonitorCallback");
+ return;
+ }
+ mExecutor.execute(() -> mPackageMonitor.doHandlePackageEvent(intent));
+ }
+ }
}
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index cb162674eb16..6489c8ed30ae 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -745,6 +745,10 @@ public interface ServiceConnector<I extends IInterface> {
boolean mAsync = false;
private String mDebugName;
{
+ // The timeout handler must be set before any calls to set timeouts on the
+ // AndroidFuture, to ensure they are posted on the proper thread.
+ setTimeoutHandler(getJobHandler());
+
long requestTimeout = getRequestTimeoutMs();
if (requestTimeout > 0) {
orTimeout(requestTimeout, TimeUnit.MILLISECONDS);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1a3804900665..66e3333acf7c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -20,7 +20,6 @@ import android.annotation.AnyThread;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
@@ -106,10 +105,14 @@ public final class InputMethodPrivilegedOperations {
*
* @param vis visibility flags
* @param backDisposition disposition flags
+ * @see android.inputmethodservice.InputMethodService#IME_ACTIVE
+ * @see android.inputmethodservice.InputMethodService#IME_VISIBLE
+ * @see android.inputmethodservice.InputMethodService#IME_INVISIBLE
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
*/
@AnyThread
- public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ public void setImeWindowStatusAsync(int vis, int backDisposition) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
diff --git a/core/java/com/android/internal/os/CpuScalingPolicies.java b/core/java/com/android/internal/os/CpuScalingPolicies.java
new file mode 100644
index 000000000000..6dbe8ab5f567
--- /dev/null
+++ b/core/java/com/android/internal/os/CpuScalingPolicies.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+
+/**
+ * CPU scaling policies: the policy IDs and corresponding supported scaling for those
+ * policies.
+ */
+public class CpuScalingPolicies {
+ private final SparseArray<int[]> mCpusByPolicy;
+ private final SparseArray<int[]> mFreqsByPolicy;
+ private final int[] mPolicies;
+ private final int mScalingStepCount;
+
+ public CpuScalingPolicies(@NonNull SparseArray<int[]> cpusByPolicy,
+ @NonNull SparseArray<int[]> freqsByPolicy) {
+ mCpusByPolicy = cpusByPolicy;
+ mFreqsByPolicy = freqsByPolicy;
+
+ mPolicies = new int[cpusByPolicy.size()];
+ for (int i = 0; i < mPolicies.length; i++) {
+ mPolicies[i] = cpusByPolicy.keyAt(i);
+ }
+
+ Arrays.sort(mPolicies);
+
+ int count = 0;
+ for (int i = freqsByPolicy.size() - 1; i >= 0; i--) {
+ count += freqsByPolicy.valueAt(i).length;
+ }
+ mScalingStepCount = count;
+ }
+
+ /**
+ * Returns available policies (aka clusters).
+ */
+ @NonNull
+ public int[] getPolicies() {
+ return mPolicies;
+ }
+
+ /**
+ * CPUs covered by the specified policy.
+ */
+ @NonNull
+ public int[] getRelatedCpus(int policy) {
+ return mCpusByPolicy.get(policy, EmptyArray.INT);
+ }
+
+ /**
+ * Scaling frequencies supported for the specified policy.
+ */
+ @NonNull
+ public int[] getFrequencies(int policy) {
+ return mFreqsByPolicy.get(policy, EmptyArray.INT);
+ }
+
+ /**
+ * Returns the overall number of supported scaling steps: grand total of available frequencies
+ * across all scaling policies.
+ */
+ public int getScalingStepCount() {
+ return mScalingStepCount;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int policy : mPolicies) {
+ sb.append("policy").append(policy)
+ .append("\n CPUs: ").append(Arrays.toString(mCpusByPolicy.get(policy)))
+ .append("\n freqs: ").append(Arrays.toString(mFreqsByPolicy.get(policy)))
+ .append("\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/core/java/com/android/internal/os/CpuScalingPolicyReader.java b/core/java/com/android/internal/os/CpuScalingPolicyReader.java
new file mode 100644
index 000000000000..c96089a5c9c9
--- /dev/null
+++ b/core/java/com/android/internal/os/CpuScalingPolicyReader.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.os.FileUtils;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.util.EmptyArray;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Captures a CPU scaling policies such as available scaling frequencies as well as
+ * CPUs (cores) for each policy.
+ *
+ * See <a
+ * href="https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html
+ * #policy-interface-in-sysfs">Policy Interface in sysfs</a>
+ */
+public class CpuScalingPolicyReader {
+ private static final String TAG = "CpuScalingPolicyReader";
+ private static final String CPUFREQ_DIR = "/sys/devices/system/cpu/cpufreq";
+ private static final Pattern POLICY_PATTERN = Pattern.compile("policy(\\d+)");
+ private static final String FILE_NAME_RELATED_CPUS = "related_cpus";
+ private static final String FILE_NAME_SCALING_AVAILABLE_FREQUENCIES =
+ "scaling_available_frequencies";
+ private static final String FILE_NAME_SCALING_BOOST_FREQUENCIES = "scaling_boost_frequencies";
+ private static final String FILE_NAME_CPUINFO_CUR_FREQ = "cpuinfo_cur_freq";
+
+ private final String mCpuFreqDir;
+
+ public CpuScalingPolicyReader() {
+ this(CPUFREQ_DIR);
+ }
+
+ @VisibleForTesting
+ public CpuScalingPolicyReader(String cpuFreqDir) {
+ mCpuFreqDir = cpuFreqDir;
+ }
+
+ /**
+ * Reads scaling policy info from sysfs files in /sys/devices/system/cpu/cpufreq
+ */
+ @NonNull
+ public CpuScalingPolicies read() {
+ SparseArray<int[]> cpusByPolicy = new SparseArray<>();
+ SparseArray<int[]> freqsByPolicy = new SparseArray<>();
+
+ File cpuFreqDir = new File(mCpuFreqDir);
+ File[] policyDirs = cpuFreqDir.listFiles();
+ if (policyDirs != null) {
+ for (File policyDir : policyDirs) {
+ Matcher matcher = POLICY_PATTERN.matcher(policyDir.getName());
+ if (matcher.matches()) {
+ int[] relatedCpus = readIntsFromFile(
+ new File(policyDir, FILE_NAME_RELATED_CPUS));
+ if (relatedCpus.length == 0) {
+ continue;
+ }
+
+ int[] availableFreqs = readIntsFromFile(
+ new File(policyDir, FILE_NAME_SCALING_AVAILABLE_FREQUENCIES));
+ int[] boostFreqs = readIntsFromFile(
+ new File(policyDir, FILE_NAME_SCALING_BOOST_FREQUENCIES));
+ int[] freqs;
+ if (boostFreqs.length == 0) {
+ freqs = availableFreqs;
+ } else {
+ freqs = Arrays.copyOf(availableFreqs,
+ availableFreqs.length + boostFreqs.length);
+ System.arraycopy(boostFreqs, 0, freqs, availableFreqs.length,
+ boostFreqs.length);
+ }
+ if (freqs.length == 0) {
+ freqs = readIntsFromFile(new File(policyDir, FILE_NAME_CPUINFO_CUR_FREQ));
+ if (freqs.length == 0) {
+ freqs = new int[]{0}; // Unknown frequency
+ }
+ }
+ int policy = Integer.parseInt(matcher.group(1));
+ cpusByPolicy.put(policy, relatedCpus);
+ freqsByPolicy.put(policy, freqs);
+ }
+ }
+ }
+
+ if (cpusByPolicy.size() == 0) {
+ // There just has to be at least one CPU - otherwise, what's executing this code?
+ cpusByPolicy.put(0, new int[]{0});
+ freqsByPolicy.put(0, new int[]{0});
+ }
+
+ return new CpuScalingPolicies(cpusByPolicy, freqsByPolicy);
+ }
+
+ @NonNull
+ private static int[] readIntsFromFile(File file) {
+ if (!file.exists()) {
+ return EmptyArray.INT;
+ }
+
+ IntArray intArray = new IntArray(16);
+ try {
+ String contents = FileUtils.readTextFile(file, 0, null).trim();
+ String[] strings = contents.split(" ");
+ intArray.clear();
+ for (String s : strings) {
+ try {
+ intArray.add(Integer.parseInt(s));
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Unexpected file format " + file
+ + ": " + contents, e);
+ }
+ }
+ return intArray.toArray();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot read " + file, e);
+ return EmptyArray.INT;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index c801be0ce3e7..21e0dc59db01 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -16,9 +16,7 @@
package com.android.internal.os;
import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.StrictMode;
import android.util.IntArray;
@@ -29,11 +27,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
-import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.CharBuffer;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -352,7 +348,7 @@ public abstract class KernelCpuUidTimeReader<T> {
private int mFreqCount = 0;
private int mErrors = 0;
private boolean mPerClusterTimesAvailable;
- private boolean mAllUidTimesAvailable = true;
+ private boolean mAllUidTimesAvailable;
public KernelCpuUidFreqTimeReader(boolean throttle) {
this(throttle, Clock.SYSTEM_CLOCK);
@@ -376,10 +372,22 @@ public abstract class KernelCpuUidTimeReader<T> {
}
/**
+ * Initializes the reader. Should be called during the system-ready boot phase.
+ */
+ public void onSystemReady() {
+ if (mBpfTimesAvailable && mCpuFreqs == null) {
+ readFreqsThroughBpf();
+ // By extension: if we can read CPU frequencies through eBPF, we can also
+ // read per-UID CPU time-in-state
+ mAllUidTimesAvailable = mCpuFreqs != null;
+ }
+ }
+
+ /**
* @return Whether per-cluster times are available.
*/
public boolean perClusterTimesAvailable() {
- return mPerClusterTimesAvailable;
+ return mBpfTimesAvailable;
}
/**
@@ -396,59 +404,6 @@ public abstract class KernelCpuUidTimeReader<T> {
return mLastTimes;
}
- /**
- * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
- * to determine if per-cluster times are available.
- *
- * @param powerProfile The PowerProfile to compare against.
- * @return A long[] of CPU frequencies in Hz.
- */
- public long[] readFreqs(@NonNull PowerProfile powerProfile) {
- checkNotNull(powerProfile);
- if (mCpuFreqs != null) {
- // No need to read cpu freqs more than once.
- return mCpuFreqs;
- }
- if (!mAllUidTimesAvailable) {
- return null;
- }
- if (mBpfTimesAvailable) {
- readFreqsThroughBpf();
- }
- if (mCpuFreqs == null) {
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
- if (readFreqs(reader.readLine()) == null) {
- return null;
- }
- } catch (IOException e) {
- if (++mErrors >= MAX_ERROR_COUNT) {
- mAllUidTimesAvailable = false;
- }
- Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- return null;
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- }
- // Check if the freqs in the proc file correspond to per-cluster freqs.
- final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
- final int numClusters = powerProfile.getNumCpuClusters();
- if (numClusterFreqs.size() == numClusters) {
- mPerClusterTimesAvailable = true;
- for (int i = 0; i < numClusters; ++i) {
- if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
- mPerClusterTimesAvailable = false;
- break;
- }
- }
- } else {
- mPerClusterTimesAvailable = false;
- }
- Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
- return mCpuFreqs;
- }
-
private long[] readFreqsThroughBpf() {
if (!mBpfTimesAvailable || mBpfReader == null) {
return null;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 2bcc645e6a9a..503e689c0d5d 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -294,6 +295,8 @@ public class PowerProfile {
private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF;
+ private static final int DEFAULT_CPU_POWER_BRACKET_NUMBER = 3;
+
/**
* A map from Power Use Item to its power consumption.
*/
@@ -357,6 +360,8 @@ public class PowerProfile {
readPowerValuesFromXml(context, xmlId);
}
initCpuClusters();
+ initCpuScalingPolicies();
+ initCpuPowerBrackets(DEFAULT_CPU_POWER_BRACKET_NUMBER);
initDisplays();
initModem();
}
@@ -452,9 +457,7 @@ public class PowerProfile {
private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster";
private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster";
private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster";
- private static final String CPU_POWER_BRACKETS_PREFIX = "cpu.power_brackets.cluster";
-
- private static final int DEFAULT_CPU_POWER_BRACKET_NUMBER = 3;
+ private static final String CPU_POWER_BRACKETS_PREFIX = "cpu.power_brackets.policy";
private void initCpuClusters() {
if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
@@ -476,8 +479,82 @@ public class PowerProfile {
mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0,
CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus);
}
+ }
- initCpuPowerBrackets(DEFAULT_CPU_POWER_BRACKET_NUMBER);
+ private SparseArray<CpuScalingPolicyPower> mCpuScalingPolicies;
+ private static final String CPU_SCALING_POLICY_POWER_POLICY = "cpu.scaling_policy_power.policy";
+ private static final String CPU_SCALING_STEP_POWER_POLICY = "cpu.scaling_step_power.policy";
+
+ private void initCpuScalingPolicies() {
+ int policyCount = 0;
+ for (String key : sPowerItemMap.keySet()) {
+ if (key.startsWith(CPU_SCALING_POLICY_POWER_POLICY)) {
+ int policy =
+ Integer.parseInt(key.substring(CPU_SCALING_POLICY_POWER_POLICY.length()));
+ policyCount = Math.max(policyCount, policy + 1);
+ }
+ }
+ for (String key : sPowerArrayMap.keySet()) {
+ if (key.startsWith(CPU_SCALING_STEP_POWER_POLICY)) {
+ int policy =
+ Integer.parseInt(key.substring(CPU_SCALING_STEP_POWER_POLICY.length()));
+ policyCount = Math.max(policyCount, policy + 1);
+ }
+ }
+
+ if (policyCount > 0) {
+ mCpuScalingPolicies = new SparseArray<>(policyCount);
+ for (int policy = 0; policy < policyCount; policy++) {
+ Double policyPower = sPowerItemMap.get(CPU_SCALING_POLICY_POWER_POLICY + policy);
+ Double[] stepPower = sPowerArrayMap.get(CPU_SCALING_STEP_POWER_POLICY + policy);
+ if (policyPower != null || stepPower != null) {
+ double[] primitiveStepPower;
+ if (stepPower != null) {
+ primitiveStepPower = new double[stepPower.length];
+ for (int i = 0; i < stepPower.length; i++) {
+ primitiveStepPower[i] = stepPower[i];
+ }
+ } else {
+ primitiveStepPower = new double[0];
+ }
+ mCpuScalingPolicies.put(policy, new CpuScalingPolicyPower(
+ policyPower != null ? policyPower : 0, primitiveStepPower));
+ }
+ }
+ } else {
+ // Legacy power_profile.xml
+ int cpuId = 0;
+ for (CpuClusterKey cpuCluster : mCpuClusters) {
+ policyCount = cpuId + 1;
+ cpuId += cpuCluster.numCpus;
+ }
+
+ if (policyCount > 0) {
+ mCpuScalingPolicies = new SparseArray<>(policyCount);
+ cpuId = 0;
+ for (CpuClusterKey cpuCluster : mCpuClusters) {
+ double clusterPower = getAveragePower(cpuCluster.clusterPowerKey);
+ double[] stepPower;
+ int numSteps = getNumElements(cpuCluster.corePowerKey);
+ if (numSteps != 0) {
+ stepPower = new double[numSteps];
+ for (int step = 0; step < numSteps; step++) {
+ stepPower[step] = getAveragePower(cpuCluster.corePowerKey, step);
+ }
+ } else {
+ stepPower = new double[1];
+ }
+ mCpuScalingPolicies.put(cpuId,
+ new CpuScalingPolicyPower(clusterPower, stepPower));
+ cpuId += cpuCluster.numCpus;
+ }
+ } else {
+ mCpuScalingPolicies = new SparseArray<>(1);
+ mCpuScalingPolicies.put(0,
+ new CpuScalingPolicyPower(getAveragePower(POWER_CPU_ACTIVE),
+ new double[]{0}));
+ }
+ }
}
/**
@@ -487,33 +564,38 @@ public class PowerProfile {
public void initCpuPowerBrackets(int defaultCpuPowerBracketNumber) {
boolean anyBracketsSpecified = false;
boolean allBracketsSpecified = true;
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- final int steps = getNumSpeedStepsInCpuCluster(cluster);
- mCpuClusters[cluster].powerBrackets = new int[steps];
- if (sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + cluster) != null) {
+ for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final int steps = cpuScalingPolicyPower.stepPower.length;
+ cpuScalingPolicyPower.powerBrackets = new int[steps];
+ if (sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy) != null) {
anyBracketsSpecified = true;
} else {
allBracketsSpecified = false;
}
}
-
if (anyBracketsSpecified && !allBracketsSpecified) {
throw new RuntimeException(
- "Power brackets should be specified for all clusters or no clusters");
+ "Power brackets should be specified for all scaling policies or none");
}
mCpuPowerBracketCount = 0;
if (allBracketsSpecified) {
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + cluster);
- if (data.length != mCpuClusters[cluster].powerBrackets.length) {
+ for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy);
+ if (data.length != cpuScalingPolicyPower.powerBrackets.length) {
throw new RuntimeException(
- "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + cluster);
+ "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy
+ + ", expected: "
+ + cpuScalingPolicyPower.powerBrackets.length);
}
- for (int i = 0; i < data.length; i++) {
- final int bracket = (int) Math.round(data[i]);
- mCpuClusters[cluster].powerBrackets[i] = bracket;
+ for (int j = 0; j < data.length; j++) {
+ final int bracket = (int) Math.round(data[j]);
+ cpuScalingPolicyPower.powerBrackets[j] = bracket;
if (bracket > mCpuPowerBracketCount) {
mCpuPowerBracketCount = bracket;
}
@@ -524,10 +606,12 @@ public class PowerProfile {
double minPower = Double.MAX_VALUE;
double maxPower = Double.MIN_VALUE;
int stateCount = 0;
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- final int steps = getNumSpeedStepsInCpuCluster(cluster);
+ for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final int steps = cpuScalingPolicyPower.stepPower.length;
for (int step = 0; step < steps; step++) {
- final double power = getAveragePowerForCpuCore(cluster, step);
+ final double power = getAveragePowerForCpuScalingStep(policy, step);
if (power < minPower) {
minPower = power;
}
@@ -541,10 +625,11 @@ public class PowerProfile {
if (stateCount <= defaultCpuPowerBracketNumber) {
mCpuPowerBracketCount = stateCount;
int bracket = 0;
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- final int steps = getNumSpeedStepsInCpuCluster(cluster);
+ for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final int steps = cpuScalingPolicyPower.stepPower.length;
for (int step = 0; step < steps; step++) {
- mCpuClusters[cluster].powerBrackets[step] = bracket++;
+ cpuScalingPolicyPower.powerBrackets[step] = bracket++;
}
}
} else {
@@ -553,27 +638,70 @@ public class PowerProfile {
final double logBracket = (Math.log(maxPower) - minLogPower)
/ defaultCpuPowerBracketNumber;
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- final int steps = getNumSpeedStepsInCpuCluster(cluster);
+ for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final int steps = cpuScalingPolicyPower.stepPower.length;
for (int step = 0; step < steps; step++) {
- final double power = getAveragePowerForCpuCore(cluster, step);
+ final double power = getAveragePowerForCpuScalingStep(policy, step);
int bracket = (int) ((Math.log(power) - minLogPower) / logBracket);
if (bracket >= defaultCpuPowerBracketNumber) {
bracket = defaultCpuPowerBracketNumber - 1;
}
- mCpuClusters[cluster].powerBrackets[step] = bracket;
+ cpuScalingPolicyPower.powerBrackets[step] = bracket;
}
}
}
}
}
+ private static class CpuScalingPolicyPower {
+ public final double policyPower;
+ public final double[] stepPower;
+ public int[] powerBrackets;
+
+ private CpuScalingPolicyPower(double policyPower, double[] stepPower) {
+ this.policyPower = policyPower;
+ this.stepPower = stepPower;
+ }
+ }
+
+ /**
+ * Returns the average additional power in (mA) when the CPU scaling policy <code>policy</code>
+ * is used.
+ *
+ * @param policy Policy ID as per <code>ls /sys/devices/system/cpu/cpufreq</code>. Typically,
+ * policy ID corresponds to the index of the first related CPU, e.g. for "policy6"
+ * <code>/sys/devices/system/cpu/cpufreq/policy6/related_cpus</code> will
+ * contain CPU IDs like <code>6, 7</code>
+ */
+ public double getAveragePowerForCpuScalingPolicy(int policy) {
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.get(policy);
+ if (cpuScalingPolicyPower != null) {
+ return cpuScalingPolicyPower.policyPower;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the average additional power in (mA) when the CPU scaling policy <code>policy</code>
+ * is used at the <code>step</code> frequency step (this is not the frequency itself, but the
+ * integer index of the frequency step).
+ */
+ public double getAveragePowerForCpuScalingStep(int policy, int step) {
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.get(policy);
+ if (cpuScalingPolicyPower != null
+ && step >= 0 && step < cpuScalingPolicyPower.stepPower.length) {
+ return cpuScalingPolicyPower.stepPower[step];
+ }
+ return 0;
+ }
+
private static class CpuClusterKey {
public final String freqKey;
public final String clusterPowerKey;
public final String corePowerKey;
public final int numCpus;
- public int[] powerBrackets;
private CpuClusterKey(String freqKey, String clusterPowerKey,
String corePowerKey, int numCpus) {
@@ -584,11 +712,19 @@ public class PowerProfile {
}
}
+ /**
+ * @deprecated Use CpuScalingPolicy instead
+ */
@UnsupportedAppUsage
+ @Deprecated
public int getNumCpuClusters() {
return mCpuClusters.length;
}
+ /**
+ * @deprecated Use CpuScalingPolicy instead
+ */
+ @Deprecated
public int getNumCoresInCpuCluster(int cluster) {
if (cluster < 0 || cluster >= mCpuClusters.length) {
return 0; // index out of bound
@@ -596,7 +732,11 @@ public class PowerProfile {
return mCpuClusters[cluster].numCpus;
}
+ /**
+ * @deprecated Use CpuScalingPolicy instead
+ */
@UnsupportedAppUsage
+ @Deprecated
public int getNumSpeedStepsInCpuCluster(int cluster) {
if (cluster < 0 || cluster >= mCpuClusters.length) {
return 0; // index out of bound
@@ -607,6 +747,10 @@ public class PowerProfile {
return 1; // Only one speed
}
+ /**
+ * @deprecated Use getAveragePowerForCpuScalingPolicy
+ */
+ @Deprecated
public double getAveragePowerForCpuCluster(int cluster) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
return getAveragePower(mCpuClusters[cluster].clusterPowerKey);
@@ -614,6 +758,10 @@ public class PowerProfile {
return 0;
}
+ /**
+ * @deprecated Use getAveragePowerForCpuScalingStep
+ */
+ @Deprecated
public double getAveragePowerForCpuCore(int cluster, int step) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
return getAveragePower(mCpuClusters[cluster].corePowerKey, step);
@@ -631,26 +779,28 @@ public class PowerProfile {
/**
* Description of a CPU power bracket: which cluster/frequency combinations are included.
*/
- public String getCpuPowerBracketDescription(int powerBracket) {
+ public String getCpuPowerBracketDescription(CpuScalingPolicies cpuScalingPolicies,
+ int powerBracket) {
StringBuilder sb = new StringBuilder();
- for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
- int[] brackets = mCpuClusters[cluster].powerBrackets;
+ for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ int[] brackets = cpuScalingPolicyPower.powerBrackets;
+ int[] freqs = cpuScalingPolicies.getFrequencies(policy);
for (int step = 0; step < brackets.length; step++) {
if (brackets[step] == powerBracket) {
if (sb.length() != 0) {
sb.append(", ");
}
- if (mCpuClusters.length > 1) {
- sb.append(cluster).append('/');
+ if (mCpuScalingPolicies.size() > 1) {
+ sb.append(policy).append('/');
}
- Double[] freqs = sPowerArrayMap.get(mCpuClusters[cluster].freqKey);
- if (freqs != null && step < freqs.length) {
- // Frequency in MHz
- sb.append(freqs[step].intValue() / 1000);
+ if (step < freqs.length) {
+ sb.append(freqs[step] / 1000);
}
sb.append('(');
sb.append(String.format(Locale.US, "%.1f",
- getAveragePowerForCpuCore(cluster, step)));
+ getAveragePowerForCpuScalingStep(policy, step)));
sb.append(')');
}
}
@@ -659,19 +809,18 @@ public class PowerProfile {
}
/**
- * Returns the CPU power bracket corresponding to the specified cluster and frequency step
+ * Returns the CPU power bracket corresponding to the specified scaling policy and frequency
+ * step
*/
- public int getPowerBracketForCpuCore(int cluster, int step) {
- if (cluster >= 0
- && cluster < mCpuClusters.length
- && step >= 0
- && step < mCpuClusters[cluster].powerBrackets.length) {
- return mCpuClusters[cluster].powerBrackets[step];
+ public int getCpuPowerBracketForScalingStep(int policy, int step) {
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.get(policy);
+ if (cpuScalingPolicyPower != null
+ && step >= 0 && step < cpuScalingPolicyPower.powerBrackets.length) {
+ return cpuScalingPolicyPower.powerBrackets[step];
}
return 0;
}
-
private int mNumDisplays;
private void initDisplays() {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index af1fdd79169a..bb868018bc95 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -232,7 +232,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private boolean mLastHasRightStableInset = false;
private boolean mLastHasLeftStableInset = false;
private int mLastWindowFlags = 0;
- private boolean mLastShouldAlwaysConsumeSystemBars = false;
+ private @InsetsType int mLastForceConsumingTypes = 0;
private @InsetsType int mLastSuppressScrimTypes = 0;
private int mRootScrollY = 0;
@@ -1111,19 +1111,19 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
: controller.getSystemBarsAppearance();
if (insets != null) {
- mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
+ mLastForceConsumingTypes = insets.getForceConsumingTypes();
- final boolean clearsCompatInsets =
- clearsCompatInsets(attrs.type, attrs.flags,
- getResources().getConfiguration().windowConfiguration
- .getWindowingMode())
- && !mLastShouldAlwaysConsumeSystemBars;
+ @InsetsType int compatInsetsTypes =
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
+ if (clearsCompatInsets(attrs.type, attrs.flags,
+ getResources().getConfiguration().windowConfiguration.getWindowingMode())) {
+ compatInsetsTypes &= mLastForceConsumingTypes;
+ }
final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars());
- final Insets systemInsets = clearsCompatInsets
+ final Insets systemInsets = compatInsetsTypes == 0
? Insets.NONE
- : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
- | WindowInsets.Type.displayCutout()), stableBarInsets);
+ : Insets.min(insets.getInsets(compatInsetsTypes), stableBarInsets);
mLastTopInset = systemInsets.top;
mLastBottomInset = systemInsets.bottom;
mLastRightInset = systemInsets.right;
@@ -1208,7 +1208,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& decorFitsSystemWindows
&& !hideNavigation)
- || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
+ || ((mLastForceConsumingTypes & WindowInsets.Type.navigationBars()) != 0
+ && hideNavigation);
boolean consumingNavBar =
((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
@@ -1224,13 +1225,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (attrs.flags & FLAG_FULLSCREEN) != 0
|| (requestedVisibleTypes & WindowInsets.Type.statusBars()) == 0;
- boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
- && decorFitsSystemWindows
- && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
- && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
- && mForceWindowDrawsBarBackgrounds
- && mLastTopInset != 0
- || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
+ boolean consumingStatusBar =
+ ((sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && decorFitsSystemWindows
+ && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
+ && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
+ && mForceWindowDrawsBarBackgrounds
+ && mLastTopInset != 0)
+ || ((mLastForceConsumingTypes & WindowInsets.Type.statusBars()) != 0
+ && fullscreen);
int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
@@ -1434,9 +1437,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private void updateColorViewInt(final ColorViewState state, int color, int dividerColor,
int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
boolean force, @InsetsType int requestedVisibleTypes) {
+ final @InsetsType int type = state.attributes.insetsType;
state.present = state.attributes.isPresent(
- (requestedVisibleTypes & state.attributes.insetsType) != 0
- || mLastShouldAlwaysConsumeSystemBars,
+ (requestedVisibleTypes & type) != 0 || (mLastForceConsumingTypes & type) != 0,
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8b9a9913183d..4f827cda6afa 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -16,7 +16,6 @@
package com.android.internal.statusbar;
-import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,9 +31,7 @@ public final class RegisterStatusBarResult implements Parcelable {
public final int mDisabledFlags1; // switch[0]
public final int mAppearance; // switch[1]
public final AppearanceRegion[] mAppearanceRegions; // switch[2]
- @InputMethodService.ImeWindowVisibility
public final int mImeWindowVis; // switch[3]
- @InputMethodService.BackDispositionMode
public final int mImeBackDisposition; // switch[4]
public final boolean mShowImeSwitcher; // switch[5]
public final int mDisabledFlags2; // switch[6]
@@ -47,12 +44,10 @@ public final class RegisterStatusBarResult implements Parcelable {
public final LetterboxDetails[] mLetterboxDetails;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
- int appearance, AppearanceRegion[] appearanceRegions,
- @InputMethodService.ImeWindowVisibility int imeWindowVis,
- @InputMethodService.BackDispositionMode int imeBackDisposition, boolean showImeSwitcher,
- int disabledFlags2, IBinder imeToken, boolean navbarColorManagedByIme, int behavior,
- int requestedVisibleTypes, String packageName, int transientBarTypes,
- LetterboxDetails[] letterboxDetails) {
+ int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
+ int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
+ boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes,
+ String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 8024a6315e04..c19265a83441 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -379,7 +379,6 @@ cc_library_shared {
"libbinary_parse",
"libdng_sdk",
"libft2",
- "libhostgraphics",
"libhwui",
"libimage_type_recognition",
"libjpeg",
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 2a670e865ced..1c597424f221 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -17,22 +17,21 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Camera-JNI"
-#include <utils/Log.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <android_runtime/android_view_Surface.h>
-
+#include <binder/IMemory.h>
+#include <camera/Camera.h>
+#include <camera/StringUtils.h>
#include <cutils/properties.h>
-#include <utils/Vector.h>
-#include <utils/Errors.h>
-
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
-#include <camera/Camera.h>
-#include <binder/IMemory.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
using namespace android;
@@ -562,7 +561,7 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj
const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
env->GetStringChars(clientPackageName, NULL));
jsize rawClientNameLen = env->GetStringLength(clientPackageName);
- String16 clientName(rawClientName, rawClientNameLen);
+ std::string clientName = toStdString(rawClientName, rawClientNameLen);
env->ReleaseStringChars(clientPackageName,
reinterpret_cast<const jchar*>(rawClientName));
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index d94b9828808b..8fc30d1c248d 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "GraphicsEnvironment"
-#include <vector>
-
#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativeloader/native_loader.h>
+
+#include <vector>
+
#include "core_jni_helpers.h"
namespace {
@@ -49,11 +50,10 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
appPackageNameChars.c_str(), vulkanVersion);
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring packageName,
- jstring devOptIn, jobjectArray featuresObj) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver,
+ jstring packageName, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars packageNameChars(env, packageName);
- ScopedUtfChars devOptInChars(env, devOptIn);
std::vector<std::string> features;
if (featuresObj != nullptr) {
@@ -73,8 +73,8 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring packa
}
}
- android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), packageNameChars.c_str(),
- devOptInChars.c_str(), features);
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver,
+ packageNameChars.c_str(), features);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -118,8 +118,7 @@ const JNINativeMethod g_methods[] = {
reinterpret_cast<void*>(setGpuStats_native)},
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
- {"setAngleInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
+ {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
reinterpret_cast<void*>(setLayerPaths_native)},
diff --git a/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
index f2df319c888d..3ac9ffba3cbf 100644
--- a/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
+++ b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
@@ -18,5 +18,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
- <solid android:color="@color/wear_material_grey_900"/>
+ <solid android:color="?attr/colorSurface"/>
</shape> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
index 4f23700a9014..b85e01dd2e06 100644
--- a/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
+++ b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
@@ -18,5 +18,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
- <solid android:color="@color/wear_material_red_mid"/>
+ <solid android:color="?attr/colorError"/>
</shape> \ No newline at end of file
diff --git a/core/res/res/layout-watch/global_actions_item.xml b/core/res/res/layout-watch/global_actions_item.xml
index f964a4a53d4f..021c9abb81f6 100644
--- a/core/res/res/layout-watch/global_actions_item.xml
+++ b/core/res/res/layout-watch/global_actions_item.xml
@@ -36,7 +36,7 @@
android:textSize="15sp"
android:letterSpacing="0.013"
android:fadingEdgeLength="12dp"
- android:textColor="@android:color/white"
+ android:textColor="?attr/textColorPrimary"
android:layout_weight="1"
android:fontFamily="google-sans-text-medium"
android:layout_width="wrap_content"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 55122ce25ea1..d80cfa340dcb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3030,15 +3030,15 @@
representation this attribute can be used for providing such. -->
<attr name="contentDescription" format="string" localization="suggested" />
- <!-- Sets the id of a view before which this one is visited in accessibility traversal.
- A screen-reader must visit the content of this view before the content of the one
- it precedes.
+ <!-- Sets the id of a view that screen readers are requested to visit after this view.
+ Requests that a screen-reader visits the content of this view before the content of the
+ one it precedes. This does nothing if either view is not important for accessibility.
{@see android.view.View#setAccessibilityTraversalBefore(int)} -->
<attr name="accessibilityTraversalBefore" format="integer" />
- <!-- Sets the id of a view after which this one is visited in accessibility traversal.
- A screen-reader must visit the content of the other view before the content of
- this one.
+ <!-- Sets the id of a view that screen readers are requested to visit before this view.
+ Requests that a screen-reader visits the content of the other view before the content
+ of this one. This does nothing if either view is not important for accessibility.
{@see android.view.View#setAccessibilityTraversalAfter(int)} -->
<attr name="accessibilityTraversalAfter" format="integer" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7f66a8342939..d828f33ca514 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3798,7 +3798,7 @@
keyboard is connected -->
<string name="show_ime">Keep it on screen while physical keyboard is active</string>
<!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=30] -->
- <string name="hardware">Show virtual keyboard</string>
+ <string name="hardware">Use on-screen keyboard</string>
<!-- Title of the notification to prompt the user to configure physical keyboard settings. [CHAR LIMIT=NOTIF_TITLE] -->
<string name="select_keyboard_layout_notification_title">Configure <xliff:g id="device_name" example="Foobar USB Keyboard">%s</xliff:g></string>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f28da1faf770..3b099e8ccafc 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -40,7 +40,7 @@
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
<!-- Argentina: 5 digits, known short codes listed -->
- <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077" />
+ <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077|78887" />
<!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
<shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
@@ -162,7 +162,7 @@
<shortcode country="jp" pattern="\\d{1,5}" free="8083" />
<!-- Kenya: 5 digits, known premium codes listed -->
- <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342" />
+ <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023" />
<!-- Kyrgyzstan: 4 digits, known premium codes listed -->
<shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
@@ -208,7 +208,7 @@
<shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
<!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="pe" pattern="\\d{4,5}" free="9963|40777" />
+ <shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
<!-- Philippines -->
<shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml
index 22571142a350..322ae05bc63e 100644
--- a/core/tests/coretests/res/xml/power_profile_test.xml
+++ b/core/tests/coretests/res/xml/power_profile_test.xml
@@ -19,12 +19,6 @@
<!-- This is the battery capacity in mAh -->
<item name="battery.capacity">3000</item>
- <!-- Number of cores each CPU cluster contains -->
- <array name="cpu.clusters.cores">
- <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
- <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
- </array>
-
<!-- Power consumption when CPU is suspended -->
<item name="cpu.suspend">5</item>
<!-- Additional power consumption when CPU is in a kernel idle loop -->
@@ -32,37 +26,21 @@
<!-- Additional power consumption by CPU excluding cluster and core when running -->
<item name="cpu.active">2.55</item>
- <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
- <item name="cpu.cluster_power.cluster0">2.11</item>
- <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
- <item name="cpu.cluster_power.cluster1">2.22</item>
-
- <!-- Different CPU speeds as reported in
- /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
- <array name="cpu.core_speeds.cluster0">
- <value>300000</value> <!-- 300 MHz CPU speed -->
- <value>1000000</value> <!-- 1000 MHz CPU speed -->
- <value>2000000</value> <!-- 2000 MHz CPU speed -->
- </array>
- <!-- Different CPU speeds as reported in
- /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
- <array name="cpu.core_speeds.cluster1">
- <value>300000</value> <!-- 300 MHz CPU speed -->
- <value>1000000</value> <!-- 1000 MHz CPU speed -->
- <value>2500000</value> <!-- 2500 MHz CPU speed -->
- <value>3000000</value> <!-- 3000 MHz CPU speed -->
- </array>
+ <!-- Additional power consumption of CPU policy0 itself when running on related cores -->
+ <item name="cpu.scaling_policy_power.policy0">2.11</item>
+ <!-- Additional power consumption of CPU policy4 itself when running on related cores -->
+ <item name="cpu.scaling_policy_power.policy3">2.22</item>
- <!-- Additional power used by a CPU from cluster 0 when running at different
- speeds. Currently this measurement also includes cluster cost. -->
- <array name="cpu.core_power.cluster0">
+ <!-- Additional power used by a CPU related to policy3 when running at different
+ speeds. -->
+ <array name="cpu.scaling_step_power.policy0">
<value>10</value> <!-- 300 MHz CPU speed -->
<value>20</value> <!-- 1000 MHz CPU speed -->
<value>30</value> <!-- 1900 MHz CPU speed -->
</array>
- <!-- Additional power used by a CPU from cluster 1 when running at different
- speeds. Currently this measurement also includes cluster cost. -->
- <array name="cpu.core_power.cluster1">
+ <!-- Additional power used by a CPU related to policy3 when running at different
+ speeds. -->
+ <array name="cpu.scaling_step_power.policy3">
<value>25</value> <!-- 300 MHz CPU speed -->
<value>35</value> <!-- 1000 MHz CPU speed -->
<value>50</value> <!-- 2500 MHz CPU speed -->
diff --git a/core/tests/coretests/res/xml/power_profile_test_cpu_legacy.xml b/core/tests/coretests/res/xml/power_profile_test_cpu_legacy.xml
new file mode 100644
index 000000000000..bd7d712b50a4
--- /dev/null
+++ b/core/tests/coretests/res/xml/power_profile_test_cpu_legacy.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<device name="Android">
+ <!-- All values are in mAh except as noted.
+ This file is for PowerProfileTest.java. Changes must be synced between these two. Since
+ power_profile.xml may be overridden by actual device's power_profile.xml at compile time,
+ this test config ensures we have something constant to test against. Values below are
+ sample values, not meant to reflect any real device.
+ -->
+
+ <!-- This is the battery capacity in mAh -->
+ <item name="battery.capacity">3000</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+ <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">5</item>
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">1.11</item>
+ <!-- Additional power consumption by CPU excluding cluster and core when running -->
+ <item name="cpu.active">2.55</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.11</item>
+ <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">2.22</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2000000</value> <!-- 2000 MHz CPU speed -->
+ </array>
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2500000</value> <!-- 2500 MHz CPU speed -->
+ <value>3000000</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used by a CPU from cluster 0 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster0">
+ <value>10</value> <!-- 300 MHz CPU speed -->
+ <value>20</value> <!-- 1000 MHz CPU speed -->
+ <value>30</value> <!-- 1900 MHz CPU speed -->
+ </array>
+ <!-- Additional power used by a CPU from cluster 1 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster1">
+ <value>25</value> <!-- 300 MHz CPU speed -->
+ <value>35</value> <!-- 1000 MHz CPU speed -->
+ <value>50</value> <!-- 2500 MHz CPU speed -->
+ <value>60</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Power used by display unit in ambient display mode, including back lighting-->
+ <item name="ambient.on">0.5</item>
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">100</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">800</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">500</item>
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">600</item>
+
+ <!-- Additional power used by the audio hardware, probably due to DSP -->
+ <item name="audio">100.0</item>
+
+ <!-- Additional power used by the video hardware, probably due to DSP -->
+ <item name="video">150.0</item> <!-- ~50mA -->
+
+ <!-- Additional power used when GPS is acquiring a signal -->
+ <item name="gps.on">10</item>
+
+ <!-- Additional power used when cellular radio is transmitting/receiving -->
+ <item name="radio.active">60</item>
+ <!-- Additional power used when cellular radio is paging the tower -->
+ <item name="radio.scanning">3</item>
+ <!-- Additional power used when the cellular radio is on. Multi-value entry,
+ one per signal strength (no signal, weak, moderate, strong) -->
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>6</value> <!-- none -->
+ <value>5</value> <!-- poor -->
+ <value>4</value> <!-- moderate -->
+ <value>3</value> <!-- good -->
+ <value>3</value> <!-- great -->
+ </array>
+</device>
diff --git a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml b/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml
index c1293880de0a..a46c6083009c 100644
--- a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml
+++ b/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml
@@ -17,34 +17,20 @@
<device name="Android">
- <array name="cpu.clusters.cores">
- <value>1</value>
- <value>2</value>
- </array>
-
- <array name="cpu.core_speeds.cluster0">
- <value>300000</value>
- </array>
-
- <array name="cpu.core_speeds.cluster1">
- <value>300000</value>
- <value>1000000</value>
- </array>
-
- <array name="cpu.core_power.cluster0">
+ <array name="cpu.scaling_step_power.policy0">
<value>10</value>
</array>
- <array name="cpu.core_power.cluster1">
+ <array name="cpu.scaling_step_power.policy4">
<value>25</value>
<value>35</value>
</array>
- <array name="cpu.power_brackets.cluster0">
+ <array name="cpu.power_brackets.policy0">
<value>1</value>
</array>
- <array name="cpu.power_brackets.cluster1">
+ <array name="cpu.power_brackets.policy4">
<value>1</value>
<value>0</value>
</array>
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 8d9461d8035d..64f5e6c68a95 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -7,3 +7,6 @@ per-file *StatusBar* = file:/packages/SystemUI/OWNERS
# A11Y and related
per-file *UiAutomation* = file:/services/accessibility/OWNERS
+
+# KeyguardManagerTest
+per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index fb3a099b0ac8..c0fdf9f2d527 100644
--- a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -20,6 +20,7 @@ import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.vibrator.persistence.VibrationXmlParser.isSupportedMimeType;
import static com.google.common.truth.Truth.assertThat;
@@ -50,6 +51,20 @@ import java.util.Map;
public class VibrationEffectXmlSerializationTest {
@Test
+ public void isSupportedMimeType_onlySupportsVibrationXmlMimeType() {
+ // Single MIME type supported
+ assertThat(isSupportedMimeType(
+ VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE)).isTrue();
+ assertThat(isSupportedMimeType("application/vnd.android.haptics.vibration+xml")).isTrue();
+ // without xml suffix not supported
+ assertThat(isSupportedMimeType("application/vnd.android.haptics.vibration")).isFalse();
+ // different top-level not supported
+ assertThat(isSupportedMimeType("haptics/vnd.android.haptics.vibration+xml")).isFalse();
+ // different type not supported
+ assertThat(isSupportedMimeType("application/vnd.android.vibration+xml")).isFalse();
+ }
+
+ @Test
public void testPrimitives_allSucceed() throws IOException {
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_CLICK)
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 925da4968517..0ebf03fab966 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
-import android.graphics.text.LineBreakConfig;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
import android.text.Layout.Alignment;
@@ -926,24 +925,4 @@ public class StaticLayoutTest {
assertEquals(0, layout.getHeight(true));
assertEquals(2, layout.getLineCount());
}
-
- @Test
- public void testBuilder_autoPhraseBreaking() {
- {
- // setAutoPhraseBreaking true
- LineBreakConfig lineBreakConfig = new LineBreakConfig.Builder()
- .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_STYLE_NONE)
- .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
- .setAutoPhraseBreaking(true)
- .build();
- final String text = "これが正解。";
- // Obtain.
- StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0,
- text.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
- builder.setLineBreakConfig(lineBreakConfig);
- builder.build();
- assertEquals(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
- builder.getLineBreakWordStyle());
- }
- }
}
diff --git a/core/tests/coretests/src/android/util/LongSparseArrayTest.java b/core/tests/coretests/src/android/util/LongSparseArrayTest.java
index bf3f0f50f4bf..247fe37b5e1f 100644
--- a/core/tests/coretests/src/android/util/LongSparseArrayTest.java
+++ b/core/tests/coretests/src/android/util/LongSparseArrayTest.java
@@ -51,4 +51,73 @@ public class LongSparseArrayTest {
.isFalse();
}
}
+
+ @Test
+ public void firstIndexOnOrAfter() {
+ final LongSparseArray<Object> longSparseArray = new LongSparseArray<>();
+
+ // Values don't matter for this test.
+ longSparseArray.put(51, new Object());
+ longSparseArray.put(10, new Object());
+ longSparseArray.put(59, new Object());
+
+ assertThat(longSparseArray.size()).isEqualTo(3);
+
+ // Testing any number arbitrarily smaller than 10.
+ assertThat(longSparseArray.firstIndexOnOrAfter(-141213)).isEqualTo(0);
+ for (long time = -43; time <= 10; time++) {
+ assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(0);
+ }
+
+ for (long time = 11; time <= 51; time++) {
+ assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(1);
+ }
+
+ for (long time = 52; time <= 59; time++) {
+ assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(2);
+ }
+
+ for (long time = 60; time <= 102; time++) {
+ assertThat(longSparseArray.firstIndexOnOrAfter(time)).isEqualTo(3);
+ }
+ // Testing any number arbitrarily larger than 59.
+ assertThat(longSparseArray.firstIndexOnOrAfter(15332)).isEqualTo(3);
+ }
+
+ @Test
+ public void lastIndexOnOrBefore() {
+ final LongSparseArray<Object> longSparseArray = new LongSparseArray<>();
+
+ // Values don't matter for this test.
+ longSparseArray.put(21, new Object());
+ longSparseArray.put(4, new Object());
+ longSparseArray.put(91, new Object());
+ longSparseArray.put(39, new Object());
+
+ assertThat(longSparseArray.size()).isEqualTo(4);
+
+ // Testing any number arbitrarily smaller than 4.
+ assertThat(longSparseArray.lastIndexOnOrBefore(-1478133)).isEqualTo(-1);
+ for (long time = -42; time < 4; time++) {
+ assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(-1);
+ }
+
+ for (long time = 4; time < 21; time++) {
+ assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(0);
+ }
+
+ for (long time = 21; time < 39; time++) {
+ assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(1);
+ }
+
+ for (long time = 39; time < 91; time++) {
+ assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(2);
+ }
+
+ for (long time = 91; time < 109; time++) {
+ assertThat(longSparseArray.lastIndexOnOrBefore(time)).isEqualTo(3);
+ }
+ // Testing any number arbitrarily larger than 91.
+ assertThat(longSparseArray.lastIndexOnOrBefore(1980732)).isEqualTo(3);
+ }
}
diff --git a/core/tests/coretests/src/android/util/TimeSparseArrayTest.java b/core/tests/coretests/src/android/util/TimeSparseArrayTest.java
deleted file mode 100644
index c8e2364c48cd..000000000000
--- a/core/tests/coretests/src/android/util/TimeSparseArrayTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link TimeSparseArray}.
- * This class only tests subclass specific functionality. Tests for the super class
- * {@link LongSparseArray} should be covered under {@link LongSparseArrayTest}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TimeSparseArrayTest {
-
- @Test
- public void closestIndexOnOrAfter() {
- final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
-
- // Values don't matter for this test.
- timeSparseArray.put(51, new Object());
- timeSparseArray.put(10, new Object());
- timeSparseArray.put(59, new Object());
-
- assertThat(timeSparseArray.size()).isEqualTo(3);
-
- // Testing any number arbitrarily smaller than 10.
- assertThat(timeSparseArray.closestIndexOnOrAfter(-141213)).isEqualTo(0);
- for (long time = -43; time <= 10; time++) {
- assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(0);
- }
-
- for (long time = 11; time <= 51; time++) {
- assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(1);
- }
-
- for (long time = 52; time <= 59; time++) {
- assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(2);
- }
-
- for (long time = 60; time <= 102; time++) {
- assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(3);
- }
- // Testing any number arbitrarily larger than 59.
- assertThat(timeSparseArray.closestIndexOnOrAfter(15332)).isEqualTo(3);
- }
-
- @Test
- public void closestIndexOnOrBefore() {
- final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
-
- // Values don't matter for this test.
- timeSparseArray.put(21, new Object());
- timeSparseArray.put(4, new Object());
- timeSparseArray.put(91, new Object());
- timeSparseArray.put(39, new Object());
-
- assertThat(timeSparseArray.size()).isEqualTo(4);
-
- // Testing any number arbitrarily smaller than 4.
- assertThat(timeSparseArray.closestIndexOnOrBefore(-1478133)).isEqualTo(-1);
- for (long time = -42; time < 4; time++) {
- assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(-1);
- }
-
- for (long time = 4; time < 21; time++) {
- assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(0);
- }
-
- for (long time = 21; time < 39; time++) {
- assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(1);
- }
-
- for (long time = 39; time < 91; time++) {
- assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(2);
- }
-
- for (long time = 91; time < 109; time++) {
- assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(3);
- }
- // Testing any number arbitrarily larger than 91.
- assertThat(timeSparseArray.closestIndexOnOrBefore(1980732)).isEqualTo(3);
- }
-}
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index b4ba23c92a22..69abf5f3204f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -40,14 +40,14 @@ public class WindowInsetsTest {
@Test
public void systemWindowInsets_afterConsuming_isConsumed() {
assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null,
- null, false, false, 0, null, null, null, null,
+ null, false, 0, 0, null, null, null, null,
WindowInsets.Type.systemBars(), false)
.consumeSystemWindowInsets().isConsumed());
}
@Test
public void multiNullConstructor_isConsumed() {
- assertTrue(new WindowInsets(null, null, null, false, false, 0, null, null, null, null,
+ assertTrue(new WindowInsets(null, null, null, false, 0, 0, null, null, null, null,
WindowInsets.Type.systemBars(), false).isConsumed());
}
@@ -63,7 +63,7 @@ public class WindowInsetsTest {
boolean[] visible = new boolean[SIZE];
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
- WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false,
+ WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, 0,
0, null, null, null, DisplayShape.NONE, systemBars(),
true /* compatIgnoreVisibility */);
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 6d635af20645..3d4918b1bd42 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -58,7 +58,7 @@ public class AccessibilityNodeInfoTest {
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 26;
+ private static final int NUM_BOOLEAN_PROPERTIES = 27;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 756888f3d91f..610b8aedc983 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -225,8 +225,16 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
}
@Override
- public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) {}
+ public void attachAccessibilityOverlayToDisplay(
+ int interactionId,
+ int displayId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback) {}
@Override
- public void attachAccessibilityOverlayToWindow(int accessibilityWindowId, SurfaceControl sc) {}
+ public void attachAccessibilityOverlayToWindow(
+ int interactionId,
+ int accessibilityWindowId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback) {}
}
diff --git a/core/tests/coretests/src/com/android/internal/content/OWNERS b/core/tests/coretests/src/com/android/internal/content/OWNERS
new file mode 100644
index 000000000000..dd9ede53239c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/OWNERS
@@ -0,0 +1,4 @@
+
+per-file PackageMonitorTest.java = file:/core/java/android/content/pm/OWNERS
+
+per-file Overlay* = file:/core/java/android/content/res/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java
new file mode 100644
index 000000000000..5290478dfbd3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/PackageMonitorTest.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * A unit test for PackageMonitor implementation.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PackageMonitorTest {
+
+ private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp";
+ private static final int FAKE_PACKAGE_UID = 123;
+ private static final int FAKE_USER_ID = 0;
+
+ @Mock
+ Context mMockContext;
+ @Mock
+ Handler mMockHandler;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testPackageMonitorMultipleRegisterThrowsException() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ spyPackageMonitor.register(mMockContext, UserHandle.ALL, false /* externalStorage */,
+ mMockHandler);
+ assertThat(spyPackageMonitor.getRegisteredHandler()).isEqualTo(mMockHandler);
+ verify(mMockContext, times(2)).registerReceiverAsUser(any(), eq(UserHandle.ALL), any(),
+ eq(null), eq(mMockHandler));
+
+ assertThrows(IllegalStateException.class,
+ () -> spyPackageMonitor.register(mMockContext, UserHandle.ALL,
+ false /* externalStorage */, mMockHandler));
+ }
+
+ @Test
+ public void testPackageMonitorRegisterMultipleUnRegisterThrowsException() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ spyPackageMonitor.register(mMockContext, UserHandle.ALL, false /* externalStorage */,
+ mMockHandler);
+ spyPackageMonitor.unregister();
+
+ assertThrows(IllegalStateException.class, spyPackageMonitor::unregister);
+ }
+
+ @Test
+ public void testPackageMonitorNotRegisterUnRegisterThrowsException() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ assertThrows(IllegalStateException.class, spyPackageMonitor::unregister);
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventUidRemoved() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_UID_REMOVED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onUidRemoved(eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageSuspended() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGES_SUSPENDED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, packageList);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onPackagesSuspended(eq(packageList));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageUnSuspended() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGES_UNSUSPENDED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, packageList);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onPackagesUnsuspended(eq(packageList));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventUserStop() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onHandleUserStop(eq(intent), eq(FAKE_USER_ID));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventExternalApplicationAvailable()
+ throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, packageList);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onPackagesAvailable(eq(packageList));
+ verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
+ eq(PackageMonitor.PACKAGE_UPDATING));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventExternalApplicationUnavailable()
+ throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, packageList);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onPackagesUnavailable(eq(packageList));
+ verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
+ eq(PackageMonitor.PACKAGE_UPDATING));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageRestarted() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onHandleForceStop(eq(intent),
+ eq(new String[]{FAKE_PACKAGE_NAME}), eq(FAKE_PACKAGE_UID), eq(true));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageQueryRestarted() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_PACKAGES, packageList);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1)).onHandleForceStop(eq(intent),
+ eq(packageList), eq(FAKE_PACKAGE_UID), eq(false));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageDataClear() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageDataCleared(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageChanged() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ String [] packageList = new String[]{FAKE_PACKAGE_NAME};
+ intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList));
+ verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageRemovedReplacing() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ intent.putExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1))
+ .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageRemovedNotReplacing()
+ throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REPLACING, false);
+ intent.putExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1))
+ .onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
+ eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageAddReplacing() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+ verify(spyPackageMonitor, times(1))
+ .onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
+ public void testPackageMonitorDoHandlePackageEventPackageAddNotReplacing() throws Exception {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REPLACING, false);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
+ eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
+ verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ public static class TestPackageMonitor extends PackageMonitor {
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/OWNERS b/core/tests/coretests/src/com/android/internal/inputmethod/OWNERS
index 5deb2ce8f24b..cbd94ba6b467 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/OWNERS
@@ -1 +1,2 @@
+# Bug component: 34867
include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
new file mode 100644
index 000000000000..7f054d136639
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+public class CpuScalingPolicyReaderTest {
+ private CpuScalingPolicyReader mCpuScalingPolicyReader;
+
+ @Before
+ public void setup() throws IOException {
+ File testDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ FileUtils.deleteContents(testDir);
+
+ File policy0 = new File(testDir, "policy0");
+ FileUtils.createDir(policy0);
+ FileUtils.stringToFile(new File(policy0, "related_cpus"), "0 2 7");
+ FileUtils.stringToFile(new File(policy0, "scaling_available_frequencies"), "1234 9876");
+
+ File policy5 = new File(testDir, "policy5");
+ FileUtils.createDir(policy5);
+ FileUtils.stringToFile(new File(policy5, "related_cpus"), "3 6\n");
+ FileUtils.stringToFile(new File(policy5, "scaling_available_frequencies"), "1234 5678\n");
+ FileUtils.stringToFile(new File(policy5, "scaling_boost_frequencies"), "9998 9999\n");
+
+ File policy7 = new File(testDir, "policy7");
+ FileUtils.createDir(policy7);
+ FileUtils.stringToFile(new File(policy7, "related_cpus"), "8\n");
+ FileUtils.stringToFile(new File(policy7, "cpuinfo_cur_freq"), "1000000");
+
+ File policy9 = new File(testDir, "policy9");
+ FileUtils.createDir(policy9);
+ FileUtils.stringToFile(new File(policy9, "related_cpus"), "42");
+
+ File policy999 = new File(testDir, "policy999");
+ FileUtils.createDir(policy999);
+
+ mCpuScalingPolicyReader = new CpuScalingPolicyReader(testDir.getPath());
+ }
+
+ @Test
+ public void readFromSysFs() {
+ CpuScalingPolicies info = mCpuScalingPolicyReader.read();
+ assertThat(info.getPolicies()).isEqualTo(new int[]{0, 5, 7, 9});
+ assertThat(info.getRelatedCpus(0)).isEqualTo(new int[]{0, 2, 7});
+ assertThat(info.getFrequencies(0)).isEqualTo(new int[]{1234, 9876});
+ assertThat(info.getRelatedCpus(5)).isEqualTo(new int[]{3, 6});
+ assertThat(info.getFrequencies(5)).isEqualTo(new int[]{1234, 5678, 9998, 9999});
+ assertThat(info.getRelatedCpus(7)).isEqualTo(new int[]{8});
+ assertThat(info.getFrequencies(7)).isEqualTo(new int[]{1000000});
+ assertThat(info.getRelatedCpus(9)).isEqualTo(new int[]{42});
+ assertThat(info.getFrequencies(9)).isEqualTo(new int[]{0}); // Unknown
+ assertThat(info.getScalingStepCount()).isEqualTo(8);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index c60a6d61ffa1..783f264d6772 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -18,7 +18,6 @@ package com.android.internal.os;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
@@ -100,63 +99,6 @@ public class KernelCpuUidFreqTimeReaderTest {
}
@Test
- public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
- final long[][] freqs = {
- {1, 12, 123, 1234},
- {1, 12, 123, 23, 123, 1234, 12345, 123456},
- {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
- {1, 12, 123, 23, 2345, 234567}
- };
- final int[] numClusters = {2, 2, 3, 1};
- final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
- for (int i = 0; i < freqs.length; ++i) {
- mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
- setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- setFreqs(freqs[i]);
- long[] actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
- Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
- assertFalse(errMsg, mReader.perClusterTimesAvailable());
-
- // Verify that a second call won't re-read the freqs
- clearFreqsAndData();
- actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- assertFalse(errMsg, mReader.perClusterTimesAvailable());
- }
- }
-
- @Test
- public void testReadFreqs_perClusterTimesAvailable() throws Exception {
- final long[][] freqs = {
- {1, 12, 123, 1234},
- {1, 12, 123, 23, 123, 1234, 12345, 123456},
- {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
- };
- final int[] numClusters = {1, 2, 3};
- final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
- for (int i = 0; i < freqs.length; ++i) {
- mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
- setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- setFreqs(freqs[i]);
- long[] actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
- Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
- assertTrue(errMsg, mReader.perClusterTimesAvailable());
-
- // Verify that a second call won't re-read the freqs
- clearFreqsAndData();
- actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- assertTrue(errMsg, mReader.perClusterTimesAvailable());
- }
- }
-
- @Test
public void testReadDelta() throws Exception {
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
@@ -170,8 +112,6 @@ public class KernelCpuUidFreqTimeReaderTest {
// Verify that readDelta also reads the frequencies if not already available.
clearFreqsAndData();
- long[] actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs, actualFreqs);
// Verify that a second call will only return deltas.
mCallback.clear();
@@ -222,8 +162,6 @@ public class KernelCpuUidFreqTimeReaderTest {
// Verify that readDelta also reads the frequencies if not already available.
clearFreqsAndData();
- long[] actualFreqs = mReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs, actualFreqs);
// Verify that a second call should still return absolute values
mCallback.clear();
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 6a3d379363a8..8fa63760d231 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -30,6 +30,7 @@ import android.annotation.XmlRes;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -71,18 +72,13 @@ public class PowerProfileTest {
public void testPowerProfile() {
mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
- assertEquals(2, mProfile.getNumCpuClusters());
- assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
- assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
- assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0));
- assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1));
- assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0));
- assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2));
- assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
- assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
+ assertEquals(2.11, mProfile.getAveragePowerForCpuScalingPolicy(0));
+ assertEquals(2.22, mProfile.getAveragePowerForCpuScalingPolicy(3));
+ assertEquals(30.0, mProfile.getAveragePowerForCpuScalingStep(0, 2));
+ assertEquals(60.0, mProfile.getAveragePowerForCpuScalingStep(3, 3));
assertEquals(3000.0, mProfile.getBatteryCapacity());
assertEquals(0.5,
mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
@@ -130,6 +126,23 @@ public class PowerProfileTest {
| ModemPowerProfile.MODEM_DRAIN_TYPE_TX
| ModemPowerProfile.MODEM_TX_LEVEL_4));
}
+ @Test
+ public void testPowerProfile_legacyCpuConfig() {
+ // This power profile has per-cluster data, rather than per-policy
+ mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_cpu_legacy);
+
+ assertEquals(2.11, mProfile.getAveragePowerForCpuScalingPolicy(0));
+ assertEquals(2.22, mProfile.getAveragePowerForCpuScalingPolicy(4));
+ assertEquals(30.0, mProfile.getAveragePowerForCpuScalingStep(0, 2));
+ assertEquals(60.0, mProfile.getAveragePowerForCpuScalingStep(4, 3));
+ assertEquals(3000.0, mProfile.getBatteryCapacity());
+ assertEquals(0.5,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
+ assertEquals(100.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
+ assertEquals(800.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
+ }
@Test
public void testModemPowerProfile_defaultRat() throws Exception {
@@ -524,15 +537,10 @@ public class PowerProfileTest {
return null;
}
- private void assertEquals(int expected, int actual) {
- Assert.assertEquals(expected, actual);
- }
-
private void assertEquals(double expected, double actual) {
Assert.assertEquals(expected, actual, 0.1);
}
-
@Test
public void powerBrackets_specifiedInPowerProfile() {
mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
@@ -541,31 +549,40 @@ public class PowerProfileTest {
int cpuPowerBracketCount = mProfile.getCpuPowerBracketCount();
assertThat(cpuPowerBracketCount).isEqualTo(2);
assertThat(new int[]{
- mProfile.getPowerBracketForCpuCore(0, 0),
- mProfile.getPowerBracketForCpuCore(1, 0),
- mProfile.getPowerBracketForCpuCore(1, 1),
+ mProfile.getCpuPowerBracketForScalingStep(0, 0),
+ mProfile.getCpuPowerBracketForScalingStep(4, 0),
+ mProfile.getCpuPowerBracketForScalingStep(4, 1),
}).isEqualTo(new int[]{1, 1, 0});
}
@Test
public void powerBrackets_automatic() {
mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[]{0, 1, 2});
+ put(3, new int[]{3, 4});
+ }},
+ new SparseArray<>() {{
+ put(0, new int[]{300000, 1000000, 2000000});
+ put(3, new int[]{300000, 1000000, 2500000, 3000000});
+ }});
assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(3);
- assertThat(mProfile.getCpuPowerBracketDescription(0))
+ assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 0))
.isEqualTo("0/300(10.0)");
- assertThat(mProfile.getCpuPowerBracketDescription(1))
- .isEqualTo("0/1000(20.0), 0/2000(30.0), 1/300(25.0)");
- assertThat(mProfile.getCpuPowerBracketDescription(2))
- .isEqualTo("1/1000(35.0), 1/2500(50.0), 1/3000(60.0)");
+ assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 1))
+ .isEqualTo("0/1000(20.0), 0/2000(30.0), 3/300(25.0)");
+ assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 2))
+ .isEqualTo("3/1000(35.0), 3/2500(50.0), 3/3000(60.0)");
assertThat(new int[]{
- mProfile.getPowerBracketForCpuCore(0, 0),
- mProfile.getPowerBracketForCpuCore(0, 1),
- mProfile.getPowerBracketForCpuCore(0, 2),
- mProfile.getPowerBracketForCpuCore(1, 0),
- mProfile.getPowerBracketForCpuCore(1, 1),
- mProfile.getPowerBracketForCpuCore(1, 2),
- mProfile.getPowerBracketForCpuCore(1, 3),
+ mProfile.getCpuPowerBracketForScalingStep(0, 0),
+ mProfile.getCpuPowerBracketForScalingStep(0, 1),
+ mProfile.getCpuPowerBracketForScalingStep(0, 2),
+ mProfile.getCpuPowerBracketForScalingStep(3, 0),
+ mProfile.getCpuPowerBracketForScalingStep(3, 1),
+ mProfile.getCpuPowerBracketForScalingStep(3, 2),
+ mProfile.getCpuPowerBracketForScalingStep(3, 3),
}).isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2});
}
@@ -576,13 +593,13 @@ public class PowerProfileTest {
assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(7);
assertThat(new int[]{
- mProfile.getPowerBracketForCpuCore(0, 0),
- mProfile.getPowerBracketForCpuCore(0, 1),
- mProfile.getPowerBracketForCpuCore(0, 2),
- mProfile.getPowerBracketForCpuCore(1, 0),
- mProfile.getPowerBracketForCpuCore(1, 1),
- mProfile.getPowerBracketForCpuCore(1, 2),
- mProfile.getPowerBracketForCpuCore(1, 3),
+ mProfile.getCpuPowerBracketForScalingStep(0, 0),
+ mProfile.getCpuPowerBracketForScalingStep(0, 1),
+ mProfile.getCpuPowerBracketForScalingStep(0, 2),
+ mProfile.getCpuPowerBracketForScalingStep(3, 0),
+ mProfile.getCpuPowerBracketForScalingStep(3, 1),
+ mProfile.getCpuPowerBracketForScalingStep(3, 2),
+ mProfile.getCpuPowerBracketForScalingStep(3, 3),
}).isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6});
}
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index a1a4265cd0a5..84dd2740e8b7 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -169,7 +169,7 @@ public class ActionBarOverlayLayoutTest {
private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
return new WindowInsets(WindowInsets.createCompatTypeMap(content.toRect()), null, null,
- false, false, 0, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
+ false, 0, 0, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
}
private ViewGroup createViewGroupWithId(int id) {
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
index 5d7fd85fc178..1da3ba6efc2b 100644
--- a/data/keyboards/Vendor_0957_Product_0001.kl
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -45,7 +45,7 @@ key 11 0
# custom keys
key usage 0x000c01BB TV_INPUT
-key usage 0x000c0186 MACRO_1
+key usage 0x000c0186 MACRO_1 WAKE
key usage 0x000c0185 TV_TELETEXT
key usage 0x000c0061 CAPTIONS
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b9d3756ac6d2..a4c655c8ce55 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -782,10 +782,13 @@ public final class Bitmap implements Parcelable {
@Nullable
public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer,
@Nullable ColorSpace colorSpace) {
- if ((hardwareBuffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ final long usage = hardwareBuffer.getUsage();
+ if ((usage & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
}
- int format = hardwareBuffer.getFormat();
+ if ((usage & HardwareBuffer.USAGE_PROTECTED_CONTENT) != 0) {
+ throw new IllegalArgumentException("Bitmap is not compatible with protected buffers");
+ }
if (colorSpace == null) {
colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index bf79b1bedd8e..7cca7f19da7d 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -30,6 +30,7 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.ArrayList;
+import java.util.Set;
/**
* A font family class can be used for creating Typeface.
@@ -58,6 +59,7 @@ import java.util.ArrayList;
*
*/
public final class FontFamily {
+
private static final String TAG = "FontFamily";
/**
@@ -73,6 +75,7 @@ public final class FontFamily {
// initial capacity.
private final SparseIntArray mStyles = new SparseIntArray(4);
+
/**
* Constructs a builder.
*
@@ -110,23 +113,63 @@ public final class FontFamily {
}
/**
+ * Build a variable font family that automatically adjust the `wght` and `ital` axes value
+ * for the requested weight/italic style values.
+ *
+ * To build a variable font family, added fonts must meet one of following conditions.
+ *
+ * If two font files are added, both font files must support `wght` axis and one font must
+ * support {@link FontStyle#FONT_SLANT_UPRIGHT} and another font must support
+ * {@link FontStyle#FONT_SLANT_ITALIC}. If the requested weight value is lower than minimum
+ * value of the supported `wght` axis, the minimum supported `wght` value is used. If the
+ * requested weight value is larger than maximum value of the supported `wght` axis, the
+ * maximum supported `wght` value is used. The weight values of the fonts are ignored.
+ *
+ * If one font file is added, that font must support the `wght` axis. If that font support
+ * `ital` axis, that `ital` value is set to 1 when the italic style is requested. If that
+ * font doesn't support `ital` axis, synthetic italic may be used. If the requested
+ * weight value is lower than minimum value of the supported `wght` axis, the minimum
+ * supported `wght` value is used. If the requested weight value is larger than maximum
+ * value of the supported `wght`axis, the maximum supported `wght` value is used. The weight
+ * value of the font is ignored.
+ *
+ * If none of the above conditions are met, this function return {@code null}.
+ *
+ * @return A variable font family. null if a variable font cannot be built from the given
+ * fonts.
+ */
+ public @Nullable FontFamily buildVariableFamily() {
+ int variableFamilyType = analyzeAndResolveVariableType(mFonts);
+ if (variableFamilyType == VARIABLE_FONT_FAMILY_TYPE_UNKNOWN) {
+ return null;
+ }
+ return build("", FontConfig.FontFamily.VARIANT_DEFAULT,
+ true /* isCustomFallback */,
+ false /* isDefaultFallback */,
+ variableFamilyType);
+ }
+
+ /**
* Build the font family
* @return a font family
*/
public @NonNull FontFamily build() {
- return build("", FontConfig.FontFamily.VARIANT_DEFAULT, true /* isCustomFallback */,
- false /* isDefaultFallback */);
+ return build("", FontConfig.FontFamily.VARIANT_DEFAULT,
+ true /* isCustomFallback */,
+ false /* isDefaultFallback */,
+ VARIABLE_FONT_FAMILY_TYPE_NONE);
}
/** @hide */
public @NonNull FontFamily build(@NonNull String langTags, int variant,
- boolean isCustomFallback, boolean isDefaultFallback) {
+ boolean isCustomFallback, boolean isDefaultFallback, int variableFamilyType) {
+
final long builderPtr = nInitBuilder();
for (int i = 0; i < mFonts.size(); ++i) {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback,
- isDefaultFallback);
+ isDefaultFallback, variableFamilyType);
final FontFamily family = new FontFamily(ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
@@ -136,11 +179,94 @@ public final class FontFamily {
return font.getStyle().getWeight() | (font.getStyle().getSlant() << 16);
}
+ /**
+ * @see #buildVariableFamily()
+ * @hide
+ */
+ public static final int VARIABLE_FONT_FAMILY_TYPE_UNKNOWN = -1;
+
+ /**
+ * @see #buildVariableFamily()
+ * @hide
+ */
+ public static final int VARIABLE_FONT_FAMILY_TYPE_NONE = 0;
+ /**
+ * @see #buildVariableFamily()
+ * @hide
+ */
+ public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY = 1;
+ /**
+ * @see #buildVariableFamily()
+ * @hide
+ */
+ public static final int VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL = 2;
+ /**
+ * @see #buildVariableFamily()
+ * @hide
+ */
+ public static final int VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT = 3;
+
+ /**
+ * The registered italic axis used for adjusting requested style.
+ * https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_ital
+ */
+ private static final int TAG_ital = 0x6974616C; // i(0x69), t(0x74), a(0x61), l(0x6c)
+
+ /**
+ * The registered weight axis used for adjusting requested style.
+ * https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_wght
+ */
+ private static final int TAG_wght = 0x77676874; // w(0x77), g(0x67), h(0x68), t(0x74)
+
+ private static int analyzeAndResolveVariableType(ArrayList<Font> fonts) {
+ if (fonts.size() > 2) {
+ return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
+ }
+
+ if (fonts.size() == 1) {
+ Font font = fonts.get(0);
+ Set<Integer> supportedAxes =
+ FontFileUtil.getSupportedAxes(font.getBuffer(), font.getTtcIndex());
+ if (supportedAxes.contains(TAG_wght)) {
+ if (supportedAxes.contains(TAG_ital)) {
+ return VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
+ } else {
+ return VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
+ }
+ } else {
+ return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
+ }
+ } else {
+ for (int i = 0; i < fonts.size(); ++i) {
+ Font font = fonts.get(i);
+ Set<Integer> supportedAxes =
+ FontFileUtil.getSupportedAxes(font.getBuffer(), font.getTtcIndex());
+ if (!supportedAxes.contains(TAG_wght)) {
+ return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
+ }
+ }
+ boolean italic1 = fonts.get(0).getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
+ boolean italic2 = fonts.get(1).getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
+
+ if (italic1 == italic2) {
+ return VARIABLE_FONT_FAMILY_TYPE_UNKNOWN;
+ } else {
+ if (italic1) {
+ // Swap fonts to make the first font upright, second font italic.
+ Font firstFont = fonts.get(0);
+ fonts.set(0, fonts.get(1));
+ fonts.set(1, firstFont);
+ }
+ return VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
+ }
+ }
+ }
+
private static native long nInitBuilder();
@CriticalNative
private static native void nAddFont(long builderPtr, long fontPtr);
private static native long nBuild(long builderPtr, String langTags, int variant,
- boolean isCustomFallback, boolean isDefaultFallback);
+ boolean isCustomFallback, boolean isDefaultFallback, int variableFamilyType);
@CriticalNative
private static native long nGetReleaseNativeFamily();
}
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index 917eef2ffede..ff38282255f2 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -19,11 +19,14 @@ package android.graphics.fonts;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.ArraySet;
import dalvik.annotation.optimization.FastNative;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Collections;
+import java.util.Set;
/**
* Provides a utility for font file operations.
@@ -62,6 +65,7 @@ public class FontFileUtil {
private static final int SFNT_VERSION_OTTO = 0x4F54544F;
private static final int TTC_TAG = 0x74746366;
private static final int OS2_TABLE_TAG = 0x4F532F32;
+ private static final int FVAR_TABLE_TAG = 0x66766172;
private static final int ANALYZE_ERROR = 0xFFFFFFFF;
@@ -200,6 +204,73 @@ public class FontFileUtil {
}
}
+ private static int getUInt16(ByteBuffer buffer, int offset) {
+ return ((int) buffer.getShort(offset)) & 0xFFFF;
+ }
+
+ /**
+ * Returns supported axes of font
+ *
+ * @param buffer A buffer of the entire font file.
+ * @param index A font index in case of font collection. Must be 0 otherwise.
+ * @return set of supported axes tag. Returns empty set on error.
+ */
+ public static Set<Integer> getSupportedAxes(@NonNull ByteBuffer buffer, int index) {
+ ByteOrder originalOrder = buffer.order();
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ try {
+ int fontFileOffset = 0;
+ int magicNumber = buffer.getInt(0);
+ if (magicNumber == TTC_TAG) {
+ // TTC file.
+ if (index >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
+ return Collections.EMPTY_SET;
+ }
+ fontFileOffset = buffer.getInt(
+ 12 /* offset to array of offsets of font files */ + 4 * index);
+ }
+ int sfntVersion = buffer.getInt(fontFileOffset);
+
+ if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
+ return Collections.EMPTY_SET;
+ }
+
+ int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
+ int fvarTableOffset = -1;
+ for (int i = 0; i < numTables; ++i) {
+ int tableOffset = fontFileOffset + 12 /* size of offset table */
+ + i * 16 /* size of table record */;
+ if (buffer.getInt(tableOffset) == FVAR_TABLE_TAG) {
+ fvarTableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
+ break;
+ }
+ }
+
+ if (fvarTableOffset == -1) {
+ // Couldn't find OS/2 table. use regular style
+ return Collections.EMPTY_SET;
+ }
+
+ if (buffer.getShort(fvarTableOffset) != 1
+ || buffer.getShort(fvarTableOffset + 2) != 0) {
+ return Collections.EMPTY_SET;
+ }
+
+ int axesArrayOffset = getUInt16(buffer, fvarTableOffset + 4);
+ int axisCount = getUInt16(buffer, fvarTableOffset + 8);
+ int axisSize = getUInt16(buffer, fvarTableOffset + 10);
+
+ ArraySet<Integer> axes = new ArraySet<>();
+ for (int i = 0; i < axisCount; ++i) {
+ axes.add(buffer.getInt(fvarTableOffset + axesArrayOffset + axisSize * i));
+ }
+
+ return axes;
+ } finally {
+ buffer.order(originalOrder);
+ }
+ }
+
@FastNative
private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
@IntRange(from = 0) int index);
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 8fe28ae731b8..3fea65f42d4f 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -194,7 +194,7 @@ public final class SystemFonts {
}
}
return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */,
- isDefaultFallback);
+ isDefaultFallback, FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE);
}
private static void appendNamedFamilyList(@NonNull FontConfig.NamedFamilyList namedFamilyList,
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index d0327159b04d..7b204f244bd8 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -18,6 +18,7 @@ package android.graphics.text;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,6 +34,32 @@ import java.util.Objects;
public final class LineBreakConfig {
/**
+ * No line break style is specified.
+ *
+ * This is a special value of line break style indicating no style value is specified.
+ * When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with
+ * {@link Builder#merge(LineBreakConfig)} function, the line break style of overridden config
+ * will be kept if the line break style of overriding config is
+ * {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
+ *
+ * <pre>
+ * val override = LineBreakConfig.Builder()
+ * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ * .build(); // UNSPECIFIED if no setLineBreakStyle is called.
+ * val config = LineBreakConfig.Builder()
+ * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT)
+ * .merge(override)
+ * .build()
+ * // Here, config has LINE_BREAK_STYLE_STRICT for line break config and
+ * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
+ * </pre>
+ *
+ * This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if this value is used for text
+ * layout/rendering.
+ */
+ public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1;
+
+ /**
* No line-break rules are used for line breaking.
*/
public static final int LINE_BREAK_STYLE_NONE = 0;
@@ -56,12 +83,38 @@ public final class LineBreakConfig {
/** @hide */
@IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = {
LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL,
- LINE_BREAK_STYLE_STRICT
+ LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED
})
@Retention(RetentionPolicy.SOURCE)
public @interface LineBreakStyle {}
/**
+ * No line break word style is specified.
+ *
+ * This is a special value of line break word style indicating no style value is specified.
+ * When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with
+ * {@link Builder#merge(LineBreakConfig)} function, the line break word style of overridden
+ * config will be kept if the line break word style of overriding config is
+ * {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
+ *
+ * <pre>
+ * val override = LineBreakConfig.Builder()
+ * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT)
+ * .build(); // UNSPECIFIED if no setLineBreakWordStyle is called.
+ * val config = LineBreakConfig.Builder()
+ * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ * .merge(override)
+ * .build()
+ * // Here, config has LINE_BREAK_STYLE_STRICT for line break config and
+ * // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
+ * </pre>
+ *
+ * This value is resolved to {@link #LINE_BREAK_WORD_STYLE_NONE} if this value is used for
+ * text layout/rendering.
+ */
+ public static final int LINE_BREAK_WORD_STYLE_UNSPECIFIED = -1;
+
+ /**
* No line-break word style is used for line breaking.
*/
public static final int LINE_BREAK_WORD_STYLE_NONE = 0;
@@ -78,7 +131,7 @@ public final class LineBreakConfig {
/** @hide */
@IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = {
- LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE
+ LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE, LINE_BREAK_WORD_STYLE_UNSPECIFIED
})
@Retention(RetentionPolicy.SOURCE)
public @interface LineBreakWordStyle {}
@@ -88,26 +141,80 @@ public final class LineBreakConfig {
*/
public static final class Builder {
// The line break style for the LineBreakConfig.
- private @LineBreakStyle int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE;
+ private @LineBreakStyle int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;
// The line break word style for the LineBreakConfig.
private @LineBreakWordStyle int mLineBreakWordStyle =
- LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
-
- // Whether or not enabling phrase breaking automatically.
- // TODO(b/226012260): Remove this and add LINE_BREAK_WORD_STYLE_PHRASE_AUTO after
- // the experiment.
- private boolean mAutoPhraseBreaking = false;
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED;
/**
* Builder constructor.
*/
public Builder() {
+ reset(null);
+ }
+
+ /**
+ * Merges line break config with other config
+ *
+ * Update the internal configurations with passed {@code config}. If the config values of
+ * passed {@code config} are unspecified, the original config values are kept. For example,
+ * the following code passes {@code config} that has {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
+ * This code generates {@link LineBreakConfig} that has line break config
+ * {@link #LINE_BREAK_STYLE_STRICT}.
+ *
+ * <pre>
+ * val override = LineBreakConfig.Builder()
+ * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ * .build(); // UNSPECIFIED if no setLineBreakStyle is called.
+ * val config = LineBreakConfig.Builder()
+ * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT)
+ * .merge(override)
+ * .build()
+ * // Here, config has LINE_BREAK_STYLE_STRICT of line break config and
+ * // LINE_BREAK_WORD_STYLE_PHRASE of line break word style.
+ * </pre>
+ *
+ * @see #LINE_BREAK_STYLE_UNSPECIFIED
+ * @see #LINE_BREAK_WORD_STYLE_UNSPECIFIED
+ *
+ * @param config an override line break config
+ * @return This {@code Builder}.
+ */
+ public @NonNull Builder merge(@NonNull LineBreakConfig config) {
+ if (config.mLineBreakStyle != LINE_BREAK_STYLE_UNSPECIFIED) {
+ mLineBreakStyle = config.mLineBreakStyle;
+ }
+ if (config.mLineBreakWordStyle != LINE_BREAK_WORD_STYLE_UNSPECIFIED) {
+ mLineBreakWordStyle = config.mLineBreakWordStyle;
+ }
+ return this;
+ }
+
+ /**
+ * Resets this builder to the given config state.
+ *
+ * @return This {@code Builder}.
+ * @hide
+ */
+ public @NonNull Builder reset(@Nullable LineBreakConfig config) {
+ if (config == null) {
+ mLineBreakStyle = LINE_BREAK_STYLE_UNSPECIFIED;
+ mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_UNSPECIFIED;
+ } else {
+ mLineBreakStyle = config.mLineBreakStyle;
+ mLineBreakWordStyle = config.mLineBreakWordStyle;
+ }
+ return this;
}
/**
* Sets the line-break style.
*
+ * Note: different from {@link #merge(LineBreakConfig)} if this function is called with
+ * {@link #LINE_BREAK_STYLE_UNSPECIFIED}, the line break style is reset to
+ * {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
+ *
* @param lineBreakStyle The new line-break style.
* @return This {@code Builder}.
*/
@@ -119,6 +226,10 @@ public final class LineBreakConfig {
/**
* Sets the line-break word style.
*
+ * Note: different from {@link #merge(LineBreakConfig)} method, if this function is called
+ * with {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}, the line break style is reset to
+ * {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
+ *
* @param lineBreakWordStyle The new line-break word style.
* @return This {@code Builder}.
*/
@@ -128,22 +239,15 @@ public final class LineBreakConfig {
}
/**
- * Enables or disables the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE}.
- *
- * @hide
- */
- public @NonNull Builder setAutoPhraseBreaking(boolean autoPhraseBreaking) {
- mAutoPhraseBreaking = autoPhraseBreaking;
- return this;
- }
-
- /**
* Builds a {@link LineBreakConfig} instance.
*
+ * This method can be called multiple times for generating multiple {@link LineBreakConfig}
+ * instances.
+ *
* @return The {@code LineBreakConfig} instance.
*/
public @NonNull LineBreakConfig build() {
- return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mAutoPhraseBreaking);
+ return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle);
}
}
@@ -164,23 +268,6 @@ public final class LineBreakConfig {
.build();
}
- /**
- * Create the LineBreakConfig instance.
- *
- * @param lineBreakStyle the line break style for text wrapping.
- * @param lineBreakWordStyle the line break word style for text wrapping.
- * @return the {@link LineBreakConfig} instance. *
- * @hide
- */
- public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle,
- @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
- LineBreakConfig.Builder builder = new LineBreakConfig.Builder();
- return builder.setLineBreakStyle(lineBreakStyle)
- .setLineBreakWordStyle(lineBreakWordStyle)
- .setAutoPhraseBreaking(autoPhraseBreaking)
- .build();
- }
-
/** @hide */
public static final LineBreakConfig NONE =
new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE)
@@ -188,7 +275,6 @@ public final class LineBreakConfig {
private final @LineBreakStyle int mLineBreakStyle;
private final @LineBreakWordStyle int mLineBreakWordStyle;
- private final boolean mAutoPhraseBreaking;
/**
* Constructor with line-break parameters.
@@ -197,10 +283,9 @@ public final class LineBreakConfig {
* {@code LineBreakConfig} instance.
*/
private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
- @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
+ @LineBreakWordStyle int lineBreakWordStyle) {
mLineBreakStyle = lineBreakStyle;
mLineBreakWordStyle = lineBreakWordStyle;
- mAutoPhraseBreaking = autoPhraseBreaking;
}
/**
@@ -213,6 +298,22 @@ public final class LineBreakConfig {
}
/**
+ * Gets the resolved line break style.
+ *
+ * This method never returns {@link #LINE_BREAK_STYLE_UNSPECIFIED}.
+ *
+ * @return The line break style.
+ * @hide
+ */
+ public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
+ if (config == null) {
+ return LINE_BREAK_STYLE_NONE;
+ }
+ return config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED
+ ? LINE_BREAK_STYLE_NONE : config.mLineBreakStyle;
+ }
+
+ /**
* Gets the current line-break word style.
*
* @return The line-break word style to be used for text wrapping.
@@ -222,14 +323,50 @@ public final class LineBreakConfig {
}
/**
- * Used to identify if the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
+ * Gets the resolved line break style.
*
- * @return The result that records whether or not the automation of
- * {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
+ * This method never returns {@link #LINE_BREAK_WORD_STYLE_UNSPECIFIED}.
+ *
+ * @return The line break word style.
* @hide
*/
- public boolean getAutoPhraseBreaking() {
- return mAutoPhraseBreaking;
+ public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
+ @Nullable LineBreakConfig config) {
+ if (config == null) {
+ return LINE_BREAK_WORD_STYLE_NONE;
+ }
+ return config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED
+ ? LINE_BREAK_WORD_STYLE_NONE : config.mLineBreakWordStyle;
+ }
+
+ /**
+ * Generates a new {@link LineBreakConfig} instance merged with given {@code config}.
+ *
+ * If values of passing {@code config} are unspecified, the original values are kept. For
+ * example, the following code shows how line break config is merged.
+ *
+ * <pre>
+ * val override = LineBreakConfig.Builder()
+ * .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ * .build(); // UNSPECIFIED if no setLineBreakStyle is called.
+ * val config = LineBreakConfig.Builder()
+ * .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_STRICT)
+ * .build();
+ *
+ * val newConfig = config.merge(override)
+ * // newConfig has LINE_BREAK_STYLE_STRICT of line break style and
+ * LINE_BREAK_WORD_STYLE_PHRASE of line break word style.
+ * </pre>
+ *
+ * @param config an overriding config.
+ * @return newly created instance that is current style merged with passed config.
+ */
+ public @NonNull LineBreakConfig merge(@NonNull LineBreakConfig config) {
+ return new LineBreakConfig(
+ config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED
+ ? mLineBreakStyle : config.mLineBreakStyle,
+ config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED
+ ? mLineBreakWordStyle : config.mLineBreakWordStyle);
}
@Override
@@ -239,12 +376,19 @@ public final class LineBreakConfig {
if (!(o instanceof LineBreakConfig)) return false;
LineBreakConfig that = (LineBreakConfig) o;
return (mLineBreakStyle == that.mLineBreakStyle)
- && (mLineBreakWordStyle == that.mLineBreakWordStyle)
- && (mAutoPhraseBreaking == that.mAutoPhraseBreaking);
+ && (mLineBreakWordStyle == that.mLineBreakWordStyle);
}
@Override
public int hashCode() {
return Objects.hash(mLineBreakStyle, mLineBreakWordStyle);
}
+
+ @Override
+ public String toString() {
+ return "LineBreakConfig{"
+ + "mLineBreakStyle=" + mLineBreakStyle
+ + ", mLineBreakWordStyle=" + mLineBreakWordStyle
+ + '}';
+ }
}
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 3f7f0880d160..fd08c8b13303 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -296,10 +296,8 @@ public class MeasuredText {
Preconditions.checkArgument(length > 0, "length can not be negative");
final int end = mCurrentOffset + length;
Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
- int lbStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakStyle() :
- LineBreakConfig.LINE_BREAK_STYLE_NONE;
- int lbWordStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakWordStyle() :
- LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+ int lbStyle = LineBreakConfig.getResolvedLineBreakStyle(lineBreakConfig);
+ int lbWordStyle = LineBreakConfig.getResolvedLineBreakWordStyle(lineBreakConfig);
nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
mCurrentOffset, end, isRtl);
mCurrentOffset = end;
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 8d20e9cee7d7..49e9d0cf8372 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -165,6 +165,68 @@ public final class PositionedGlyphs {
}
/**
+ * Returns true if the fake bold option used for drawing, otherwise false.
+ *
+ * @param index the glyph index
+ * @return true if the fake bold option is on, otherwise off.
+ */
+ public boolean getFakeBold(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetFakeBold(mLayoutPtr, index);
+ }
+
+ /**
+ * Returns true if the fake italic option used for drawing, otherwise false.
+ *
+ * @param index the glyph index
+ * @return true if the fake italic option is on, otherwise off.
+ */
+ public boolean getFakeItalic(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetFakeItalic(mLayoutPtr, index);
+ }
+
+ /**
+ * A special value returned by {@link #getWeightOverride(int)} and
+ * {@link #getItalicOverride(int)} that indicates no font variation setting is overridden.
+ */
+ public static final float NO_OVERRIDE = Float.MIN_VALUE;
+
+ /**
+ * Returns overridden weight value if the font is variable font and `wght` value is overridden
+ * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
+ *
+ * @param index the glyph index
+ * @return overridden weight value or {@link #NO_OVERRIDE}.
+ */
+ public float getWeightOverride(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ float value = nGetWeightOverride(mLayoutPtr, index);
+ if (value == -1) {
+ return NO_OVERRIDE;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Returns overridden italic value if the font is variable font and `ital` value is overridden
+ * for drawing. Otherwise returns {@link #NO_OVERRIDE}.
+ *
+ * @param index the glyph index
+ * @return overridden weight value or {@link #NO_OVERRIDE}.
+ */
+ public float getItalicOverride(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ float value = nGetItalicOverride(mLayoutPtr, index);
+ if (value == -1) {
+ return NO_OVERRIDE;
+ } else {
+ return value;
+ }
+ }
+
+ /**
* Create single style layout from native result.
*
* @hide
@@ -210,6 +272,14 @@ public final class PositionedGlyphs {
private static native long nGetFont(long minikinLayout, int i);
@CriticalNative
private static native long nReleaseFunc();
+ @CriticalNative
+ private static native boolean nGetFakeBold(long minikinLayout, int i);
+ @CriticalNative
+ private static native boolean nGetFakeItalic(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetWeightOverride(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetItalicOverride(long minikinLayout, int i);
@Override
public boolean equals(Object o) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index fe5432fcd9d8..fc5f7d6f399e 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1351,7 +1351,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* the key, it is also irreversibly invalidated once a new biometric is enrolled or once\
* no more biometrics are enrolled, unless {@link
* #setInvalidatedByBiometricEnrollment(boolean)} is used to allow validity after
- * enrollment. Attempts to initialize cryptographic operations using such keys will throw
+ * enrollment, or {@code KeyProperties.AUTH_DEVICE_CREDENTIAL} is specified as part of
+ * the parameters to {@link #setUserAuthenticationParameters}.
+ * Attempts to initialize cryptographic operations using such keys will throw
* {@link KeyPermanentlyInvalidatedException}.</li>
* </ul>
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 24aaa9b75ebe..73eb62ae47e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -339,19 +339,36 @@ public class PipTransition extends PipTransitionController {
}
// This means an expand happened before enter-pip finished and we are now "merging" a
// no-op transition that happens to match our exit-pip.
+ // Or that the keyguard is up and preventing the transition from applying, in which case we
+ // want to manually reset pip. (b/283783868)
boolean cancelled = false;
if (mPipAnimationController.getCurrentAnimator() != null) {
mPipAnimationController.getCurrentAnimator().cancel();
+ mPipAnimationController.resetAnimatorState();
cancelled = true;
}
+
// Unset exitTransition AFTER cancel so that finishResize knows we are merging.
mExitTransition = null;
- if (!cancelled || aborted) return;
+ if (!cancelled) return;
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
- startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
- mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
- new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ if (aborted) {
+ // keyguard case - the transition got aborted, so we want to reset state and
+ // windowing mode before reapplying the resize transaction
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_LEAVE_PIP);
+ mPipOrganizer.onExitPipFinished(taskInfo);
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
+ wct.setBounds(taskInfo.token, null);
+ mPipOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_LEAVE_PIP, false);
+ } else {
+ // merge case
+ startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
+ mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
+ new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ }
}
mExitDestinationBounds.setEmpty();
mCurrentPipTaskToken = null;
@@ -567,7 +584,16 @@ public class PipTransition extends PipTransitionController {
mPipBoundsState.getDisplayBounds());
mFinishCallback = (wct, wctCB) -> {
mPipOrganizer.onExitPipFinished(taskInfo);
- if (!Transitions.SHELL_TRANSITIONS_ROTATION && toFullscreen) {
+
+ // TODO(b/286346098): remove the OPEN app flicker completely
+ // not checking if we go to fullscreen helps avoid getting pip into an inconsistent
+ // state after the flicker occurs. This is a temp solution until flicker is removed.
+ if (!Transitions.SHELL_TRANSITIONS_ROTATION) {
+ // will help to debug the case when we are not exiting to fullscreen
+ if (!toFullscreen) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: startExitAnimation() not exiting to fullscreen", TAG);
+ }
wct = wct != null ? wct : new WindowContainerTransaction();
wct.setBounds(pipTaskToken, null);
mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
@@ -831,7 +857,7 @@ public class PipTransition extends PipTransitionController {
}
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect currentBounds = pipChange.getStartAbsBounds();
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
@@ -1050,7 +1076,7 @@ public class PipTransition extends PipTransitionController {
// When the PIP window is visible and being a part of the transition, such as display
// rotation, we need to update its bounds and rounded corner.
final SurfaceControl leash = pipChange.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
+ final Rect destBounds = mPipOrganizer.getCurrentOrAnimatingBounds();
final boolean isInPip = mPipTransitionState.isInPip();
mSurfaceTransactionHelper
.crop(startTransaction, leash, destBounds)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 4a06d84ce90d..8c2879e0d816 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -383,6 +383,9 @@ public class PipAccessibilityInteractionConnection {
}
@Override
- public void attachAccessibilityOverlayToWindow(SurfaceControl sc) {}
+ public void attachAccessibilityOverlayToWindow(
+ SurfaceControl sc,
+ int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 843e5af67af9..837f11803ab2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -567,7 +567,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
&& taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
// Tasks that are always on top (e.g. bubbles), will handle their own transition
// as they are on top of everything else. So cancel the merge here.
- cancel("task #" + taskInfo.taskId + " is always_on_top");
+ cancel(false /* toHome */, false /* withScreenshots */,
+ "task #" + taskInfo.taskId + " is always_on_top");
return;
}
final boolean isRootTask = taskInfo != null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index acc1c5eb74b6..bf70d48e5801 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2853,18 +2853,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ final ArrayMap<Integer, SurfaceControl> dismissingTasks = new ArrayMap<>();
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null) continue;
+ if (getStageOfTask(taskInfo) != null
+ || getSplitItemPosition(change.getLastParent()) != SPLIT_POSITION_UNDEFINED) {
+ dismissingTasks.put(taskInfo.taskId, change.getLeash());
+ }
+ }
+
+
if (shouldBreakPairedTaskInRecents(dismissReason)) {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
mRecentTasks.ifPresent(recentTasks -> {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null && (getStageOfTask(taskInfo) != null
- || getSplitItemPosition(change.getLastParent())
- != SPLIT_POSITION_UNDEFINED)) {
- recentTasks.removeSplitPair(taskInfo.taskId);
- }
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ recentTasks.removeSplitPair(dismissingTasks.keyAt(i));
}
});
}
@@ -2882,6 +2888,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
t.setPosition(toStage == STAGE_TYPE_MAIN
? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
+ } else {
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ finishT.hide(dismissingTasks.valueAt(i));
+ }
}
if (toStage == STAGE_TYPE_UNDEFINED) {
@@ -2891,7 +2901,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
// Hide divider and dim layer on transition finished.
- setDividerVisibility(false, finishT);
+ setDividerVisibility(false, t);
finishT.hide(mMainStage.mDimLayer);
finishT.hide(mSideStage.mDimLayer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index c964df1452e0..c2f15f6cba75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.graphics.Color.WHITE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -77,6 +78,13 @@ public class TaskSnapshotWindow {
@NonNull Runnable clearWindowHandler) {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final int taskId = runningTaskInfo.taskId;
+
+ // if we're in PIP we don't want to create the snapshot
+ if (runningTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "did not create taskSnapshot due to being in PIP");
+ return null;
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"create taskSnapshot surface for task: %d", taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index a28ce55e8c94..d9edde16a863 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -25,6 +25,7 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -500,6 +501,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
}
}
+ mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
finishCB);
// Dispatch the rest of the transition normally. This will most-likely be taken by
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 d8a88770072d..75659960bc32 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
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -728,11 +729,15 @@ public class Transitions implements RemoteCallable<Transitions>,
final int changeSize = info.getChanges().size();
boolean taskChange = false;
boolean transferStartingWindow = false;
+ int noAnimationBehindStartingWindow = 0;
boolean allOccluded = changeSize > 0;
for (int i = changeSize - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
taskChange |= change.getTaskInfo() != null;
transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
+ if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)) {
+ noAnimationBehindStartingWindow++;
+ }
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
}
@@ -740,9 +745,11 @@ public class Transitions implements RemoteCallable<Transitions>,
// There does not need animation when:
// A. Transfer starting window. Apply transfer starting window directly if there is no other
// task change. Since this is an activity->activity situation, we can detect it by selecting
- // transitions with only 2 changes where neither are tasks and one is a starting-window
- // recipient.
- if (!taskChange && transferStartingWindow && changeSize == 2
+ // transitions with only 2 changes where
+ // 1. neither are tasks, and
+ // 2. one is a starting-window recipient, or all change is behind starting window.
+ if (!taskChange && (transferStartingWindow || noAnimationBehindStartingWindow == changeSize)
+ && changeSize == 2
// B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
// changes are underneath another change.
|| ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index 27eaa40ee49b..fd56a6e49d3e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.graphics.Point
@@ -40,8 +40,6 @@ import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
internal object SplitScreenUtils {
@@ -114,13 +112,12 @@ internal object SplitScreenUtils {
}
fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
) {
val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
@@ -326,14 +323,14 @@ internal object SplitScreenUtils {
dividerBar.drag(
Point(
if (dragToRight) {
- displayBounds.width * 4 / 5
+ displayBounds.right
} else {
- displayBounds.width * 1 / 5
+ displayBounds.left
},
if (dragToBottom) {
- displayBounds.height * 4 / 5
+ displayBounds.bottom
} else {
- displayBounds.height * 1 / 5
+ displayBounds.top
}
)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
new file mode 100644
index 000000000000..6fe88cacbbc7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching to letterboxed app from launcher
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:QuickSwitchLauncherToLetterboxAppTest`
+ *
+ * Actions:
+ * ```
+ * Launch a letterboxed app
+ * Navigate home to show launcher
+ * Swipe right from the bottom of the screen to quick switch back to the app
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) :
+ BaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+
+ letterboxApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(letterboxApp)
+ .waitForAndVerify()
+
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(letterboxApp)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
+ teardown { letterboxApp.exit(wmHelper) }
+ }
+
+ /**
+ * Checks that [letterboxApp] is the top window at the end of the transition once we have fully
+ * quick switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppBeingOnTop() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(letterboxApp) }
+ }
+
+ /** Checks that the transition starts with the home activity being tagged as visible. */
+ @Postsubmit
+ @Test
+ fun startsWithHomeActivityFlaggedVisible() {
+ flicker.assertWmStart { this.isHomeActivityVisible() }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] windows
+ * filling/covering exactly display size
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherWindowsCoverFullScreen() {
+ flicker.assertWmStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] layers
+ * filling/covering exactly the display size.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherLayersCoverFullScreen() {
+ flicker.assertLayersStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] being the top
+ * window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherBeingOnTop() {
+ flicker.assertWmStart { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) }
+ }
+
+ /**
+ * Checks that the transition ends with the home activity being flagged as not visible. By this
+ * point we should have quick switched away from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithHomeActivityFlaggedInvisible() {
+ flicker.assertWmEnd { this.isHomeActivityInvisible() }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s window starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowBecomesAndStaysVisible() {
+ flicker.assertWm {
+ this.isAppWindowInvisible(letterboxApp)
+ .then()
+ .isAppWindowVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s layer starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerBecomesAndStaysVisible() {
+ flicker.assertLayers { this.isInvisible(letterboxApp).then().isVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherWindowBecomesAndStaysInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherLayerBecomesAndStaysInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is visible at least until the app
+ * window is visible. Ensures that at any point, either the launcher or [letterboxApp] windows
+ * are at least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is visible at least until the app layer
+ * is visible. Ensures that at any point, either the launcher or [letterboxApp] layers are at
+ * least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LETTERBOX] layer is visible as soon as the
+ * [letterboxApp] layer is visible at the end of the transition once we have fully quick
+ * switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun appAndLetterboxLayersBothVisibleOnceLauncherIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ .isVisible(ComponentNameMatcher.LETTERBOX) }
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index 36bbafb4c05e..8a85374d0712 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -16,12 +16,9 @@
package com.android.wm.shell.flicker.pip
-import android.app.Instrumentation
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
@@ -29,13 +26,9 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.UiObject2
-import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -73,13 +66,11 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
- protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
- fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
- SimpleAppHelper(
- instrumentation,
- ActivityOptions.SplitScreen.Primary.LABEL,
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
- )
+ private val secondAppForSplitScreen = SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -88,14 +79,7 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- enterSplitScreen()
- // wait until split screen is established
- wmHelper
- .StateSyncBuilder()
- .withWindowSurfaceAppeared(pipApp)
- .withWindowSurfaceAppeared(secondAppForSplitScreen)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -107,46 +91,6 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
transitions { tapl.goHome() }
}
- // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils
- private val TIMEOUT_MS = 3_000L
- private val overviewSnapshotSelector: BySelector
- get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot")
- private fun enterSplitScreen() {
- // 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's currentTask on tablet is sometimes not what we expected if the overview
- // contains more than 3 task views. We need to use uiautomator directly to find the
- // second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
- val snapshots =
- tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
- if (snapshots == null || snapshots.size < 1) {
- error("Fail to find a overview snapshot to split.")
- }
-
- // Find the second task in the upper right corner in split select mode by sorting
- // 'left' in descending order and 'top' in ascending order.
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t2.getVisibleBounds().left - t1.getVisibleBounds().left
- }
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t1.getVisibleBounds().top - t2.getVisibleBounds().top
- }
- snapshots[0].click()
- } else {
- tapl.workspace
- .switchToOverview()
- .currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
- }
- SystemClock.sleep(TIMEOUT_MS)
- }
-
@Presubmit
@Test
override fun pipOverlayLayerAppearThenDisappear() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
new file mode 100644
index 000000000000..76ad6b9bc49c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class CopyContentInSplit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val textEditApp = SplitScreenUtils.getIme(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ }
+
+ @Test
+ open fun copyContentInSplit() {
+ SplitScreenUtils.copyContentInSplit(instrumentation, device, primaryApp, textEditApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
new file mode 100644
index 000000000000..25182b40a300
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByDivider() {
+ if (tapl.isTablet) {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = false,
+ dragToBottom = true
+ )
+ } else {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = true,
+ dragToBottom = true
+ )
+ }
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
new file mode 100644
index 000000000000..000b628b5ff6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByGoHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByGoHome() {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
new file mode 100644
index 000000000000..dd9ff3c7f64f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DragDividerToResize
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dragDividerToResize() {
+ SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
new file mode 100644
index 000000000000..4bbb9aa07911
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromAllApps
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromAllApps() {
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
new file mode 100644
index 000000000000..a2b75267b662
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromNotification
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ sendNotificationApp.postNotification(wmHelper)
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromNotification() {
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ sendNotificationApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
new file mode 100644
index 000000000000..1ccd8133c234
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromShortcut
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(tapl.isTablet)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromShortcut() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .openDeepShortcutMenu()
+ .getMenuItem("Split Screen Secondary Activity")
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ // TODO: Do we want this check in here? Add to the other tests?
+ // flicker.splitScreenEntered(
+ // primaryApp,
+ // secondaryApp,
+ // fromOtherApp = false,
+ // appExistAtStart = false
+ // )
+ }
+
+ @After
+ fun teardwon() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 000000000000..664786b9e523
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromTaskbar
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromTaskbar() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
new file mode 100644
index 000000000000..88fd0841b174
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenFromOverview
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ @Test
+ open fun enterSplitScreenFromOverview() {
+ SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
new file mode 100644
index 000000000000..83a18e8d0b49
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
@@ -0,0 +1,422 @@
+/*
+ * 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.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.os.SystemClock
+import android.platform.test.rule.NavigationModeRule
+import android.platform.test.rule.PressHomeRule
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.IComponentMatcher
+import android.tools.common.traces.component.IComponentNameMatcher
+import android.tools.device.apphelpers.MessagingAppHelper
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.device.flicker.rules.LaunchAppRule
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.NotificationAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+import org.junit.Assert.assertNotNull
+import org.junit.rules.RuleChain
+
+object SplitScreenUtils {
+ private const val TIMEOUT_MS = 3_000L
+ private const val DRAG_DURATION_MS = 1_000L
+ private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ private const val DIVIDER_BAR = "docked_divider_handle"
+ private const val OVERVIEW_SNAPSHOT = "snapshot"
+ private const val GESTURE_STEP_MS = 16L
+ private val LONG_PRESS_TIME_MS = ViewConfiguration.getLongPressTimeout() * 2L
+ private val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
+
+ private val notificationScrollerSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
+ private val notificationContentSelector: BySelector
+ get() = By.text("Flicker Test Notification")
+ private val dividerBarSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
+ private val overviewSnapshotSelector: BySelector
+ get() = By.res(LAUNCHER_UI_PACKAGE_NAME, OVERVIEW_SNAPSHOT)
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ fun testSetupRule(navigationMode: () -> NavBar, rotation: () -> Rotation): RuleChain {
+ return RuleChain.outerRule(UnlockScreenRule())
+ .around(
+ NavigationModeRule(
+ navigationMode().value,
+ /* changeNavigationModeAfterTest */ false
+ )
+ )
+ .around(
+ LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
+ )
+ .around(RemoveAllTasksButHomeRule())
+ .around(
+ ChangeDisplayOrientationRule(
+ rotation(),
+ resetOrientationAfterTest = false,
+ clearCacheAfterParsing = false
+ )
+ )
+ .around(PressHomeRule())
+ }
+
+ fun getPrimary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getSecondary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Secondary.LABEL,
+ ActivityOptions.SplitScreen.Secondary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getNonResizeable(instrumentation: Instrumentation): NonResizeableAppHelper =
+ NonResizeableAppHelper(instrumentation)
+
+ fun getSendNotification(instrumentation: Instrumentation): NotificationAppHelper =
+ NotificationAppHelper(instrumentation)
+
+ fun getIme(instrumentation: Instrumentation): ImeAppHelper = ImeAppHelper(instrumentation)
+
+ fun waitForSplitComplete(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: IComponentMatcher,
+ secondaryApp: IComponentMatcher,
+ ) {
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(primaryApp)
+ .withWindowSurfaceAppeared(secondaryApp)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
+ }
+
+ fun enterSplit(
+ wmHelper: WindowManagerStateHelper,
+ tapl: LauncherInstrumentation,
+ device: UiDevice,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ splitFromOverview(tapl, device)
+ waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
+ // 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's currentTask on tablet is sometimes not what we expected if the overview
+ // contains more than 3 task views. We need to use uiautomator directly to find the
+ // second task to split.
+ tapl.workspace.switchToOverview().overviewActions.clickSplit()
+ val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
+ if (snapshots == null || snapshots.size < 1) {
+ error("Fail to find a overview snapshot to split.")
+ }
+
+ // Find the second task in the upper right corner in split select mode by sorting
+ // 'left' in descending order and 'top' in ascending order.
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t2.getVisibleBounds().left - t1.getVisibleBounds().left
+ }
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ }
+ snapshots[0].click()
+ } else {
+ tapl.workspace
+ .switchToOverview()
+ .currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras =
+ mapOf(ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ fun dragFromNotificationToSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+
+ // Pull down the notifications
+ device.swipe(
+ displayBounds.centerX(),
+ 5,
+ displayBounds.centerX(),
+ displayBounds.bottom,
+ 50 /* steps */
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+
+ // Find the target notification
+ val notificationScroller =
+ device.wait(Until.findObject(notificationScrollerSelector), TIMEOUT_MS)
+ ?: error("Unable to find view $notificationScrollerSelector")
+ var notificationContent = notificationScroller.findObject(notificationContentSelector)
+
+ while (notificationContent == null) {
+ device.swipe(
+ displayBounds.centerX(),
+ displayBounds.centerY(),
+ displayBounds.centerX(),
+ displayBounds.centerY() - 150,
+ 20 /* steps */
+ )
+ notificationContent = notificationScroller.findObject(notificationContentSelector)
+ }
+
+ // Drag to split
+ val dragStart = notificationContent.visibleCenter
+ val dragMiddle = Point(dragStart.x + 50, dragStart.y)
+ val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
+ val downTime = SystemClock.uptimeMillis()
+
+ touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, dragStart)
+ // It needs a horizontal movement to trigger the drag
+ touchMove(
+ instrumentation,
+ downTime,
+ SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS,
+ dragStart,
+ dragMiddle
+ )
+ touchMove(
+ instrumentation,
+ downTime,
+ SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS,
+ dragMiddle,
+ dragEnd
+ )
+ // Wait for a while to start splitting
+ SystemClock.sleep(TIMEOUT_MS)
+ touch(
+ instrumentation,
+ MotionEvent.ACTION_UP,
+ downTime,
+ SystemClock.uptimeMillis(),
+ GESTURE_STEP_MS,
+ dragEnd
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun touch(
+ instrumentation: Instrumentation,
+ action: Int,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ point: Point
+ ) {
+ val motionEvent =
+ MotionEvent.obtain(downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0)
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
+ motionEvent.recycle()
+ SystemClock.sleep(duration)
+ }
+
+ fun touchMove(
+ instrumentation: Instrumentation,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ from: Point,
+ to: Point
+ ) {
+ val steps: Long = duration / GESTURE_STEP_MS
+ var currentTime = eventTime
+ var currentX = from.x.toFloat()
+ var currentY = from.y.toFloat()
+ val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
+ val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
+
+ for (i in 1..steps) {
+ val motionMove =
+ MotionEvent.obtain(
+ downTime,
+ currentTime,
+ MotionEvent.ACTION_MOVE,
+ currentX,
+ currentY,
+ 0
+ )
+ motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionMove, true)
+ motionMove.recycle()
+
+ currentTime += GESTURE_STEP_MS
+ if (i == steps - 1) {
+ currentX = to.x.toFloat()
+ currentY = to.y.toFloat()
+ } else {
+ currentX += stepX
+ currentY += stepY
+ }
+ SystemClock.sleep(GESTURE_STEP_MS)
+ }
+ }
+
+ fun createShortcutOnHotseatIfNotExist(tapl: LauncherInstrumentation, appName: String) {
+ tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0))
+ val allApps = tapl.workspace.switchToAllApps()
+ allApps.freeze()
+ try {
+ allApps.getAppIcon(appName).dragToHotseat(0)
+ } finally {
+ allApps.unfreeze()
+ }
+ }
+
+ fun dragDividerToResizeAndWait(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3), 200)
+
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER)
+ .waitForAndVerify()
+ }
+
+ fun dragDividerToDismissSplit(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper,
+ dragToRight: Boolean,
+ dragToBottom: Boolean
+ ) {
+ val displayBounds =
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(
+ Point(
+ if (dragToRight) {
+ displayBounds.width * 4 / 5
+ } else {
+ displayBounds.width * 1 / 5
+ },
+ if (dragToBottom) {
+ displayBounds.height * 4 / 5
+ } else {
+ displayBounds.height * 1 / 5
+ }
+ )
+ )
+ }
+
+ fun doubleTapDividerToSwitch(device: UiDevice) {
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ val interval =
+ (ViewConfiguration.getDoubleTapTimeout() + ViewConfiguration.getDoubleTapMinTime()) / 2
+ dividerBar.click()
+ SystemClock.sleep(interval.toLong())
+ dividerBar.click()
+ }
+
+ fun copyContentInSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ sourceApp: IComponentNameMatcher,
+ destinationApp: IComponentNameMatcher,
+ ) {
+ // Copy text from sourceApp
+ val textView =
+ device.wait(
+ Until.findObject(By.res(sourceApp.packageName, "SplitScreenTest")),
+ TIMEOUT_MS
+ )
+ assertNotNull("Unable to find the TextView", textView)
+ textView.click(LONG_PRESS_TIME_MS)
+
+ val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
+ assertNotNull("Unable to find the copy button", copyBtn)
+ copyBtn.click()
+
+ // Paste text to destinationApp
+ val editText =
+ device.wait(
+ Until.findObject(By.res(destinationApp.packageName, "plain_text_input")),
+ TIMEOUT_MS
+ )
+ assertNotNull("Unable to find the EditText", editText)
+ editText.click(LONG_PRESS_TIME_MS)
+
+ val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
+ assertNotNull("Unable to find the paste button", pasteBtn)
+ pasteBtn.click()
+
+ // Verify text
+ if (!textView.text.contentEquals(editText.text)) {
+ error("Fail to copy content in split")
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 000000000000..e5501f4c6cd2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchAppByDoubleTapDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun switchAppByDoubleTapDivider() {
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appWindowsSwitched") {
+ val primaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+ } else {
+ primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ } else {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appLayersSwitched") {
+ val primaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+ val secondaryVisibleRegion =
+ secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryVisibleRegion.right <= primaryVisibleRegion.left
+ } else {
+ primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ } else {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun isLandscape(rotation: Rotation): Boolean {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return displayBounds.width > displayBounds.height
+ }
+
+ private fun isTablet(): Boolean {
+ val sizeDp: Point = device.displaySizeDp
+ val LARGE_SCREEN_DP_THRESHOLD = 600
+ return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
new file mode 100644
index 000000000000..b3f1e87380e4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromAnotherApp
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromAnotherApp() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
new file mode 100644
index 000000000000..d1121162c267
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromHome() {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
new file mode 100644
index 000000000000..9ab924ca46f6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromRecent
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromRecent() {
+ tapl.workspace.switchToOverview().currentTask.open()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
new file mode 100644
index 000000000000..b694dfa7f384
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBetweenSplitPairs
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getIme(instrumentation)
+ private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+ }
+
+ @Test
+ open fun switchBetweenSplitPairs() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
new file mode 100644
index 000000000000..f78b7881735a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class UnlockKeyguardToSplitScreen {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule =
+ SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { Rotation.ROTATION_0 })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+
+ SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun unlockKeyguardToSplitScreen() {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ device.wakeUp()
+ device.pressMenu()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 580b153421a4..d3434a5b18e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -21,6 +21,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseBenchmarkTest
+import com.android.wm.shell.flicker.SplitScreenUtils
abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index d1ca9eac198d..9c68aa488d65 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -25,7 +25,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 73acb1f0cc47..21ac7839c2f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -25,7 +25,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index 86ffd2af6748..931bff6f97e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -25,7 +25,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index dfde3b669813..7fa2c0bba2be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -24,7 +24,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index d13e4134a961..952051f62a92 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index 1d4166922b13..1de1c0c5e91e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index b4bafa79cd48..929c7eab3105 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index da44ecdb9304..9f829c9dc46e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index af06d6da2518..1d5518f319d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -25,7 +25,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 23156b5d1628..a7fb93e9b645 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -28,7 +28,7 @@ import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 2d810d3e2631..8358aff00213 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index f6df1e42d1b7..b63c7659c894 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index ba46bdcdad21..ce5a409b2756 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -26,7 +26,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 0d871e500688..9821bfac7a74 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -24,7 +24,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,8 +64,7 @@ open class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlickerT
thisTransition(this)
}
- @PlatinumTest(focusArea = "sysui")
- @Presubmit @Test open fun cujCompleted() {}
+ @PlatinumTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 7952b7125a34..4fc4627093db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -23,7 +23,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index 69774cc6e133..af1668fbd7dd 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -89,7 +89,8 @@ static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) {
}
std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
builder->langId, builder->variant, std::move(builder->fonts),
- true /* isCustomFallback */, false /* isDefaultFallback */);
+ true /* isCustomFallback */, false /* isDefaultFallback */,
+ minikin::VariationFamilyType::None);
if (family->getCoverage().length() == 0) {
return 0;
}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index ee158ee4e316..1e392b14728d 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -60,7 +60,7 @@ static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPt
// Regular JNI
static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr,
jstring langTags, jint variant, jboolean isCustomFallback,
- jboolean isDefaultFallback) {
+ jboolean isDefaultFallback, jint variationFamilyType) {
std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
uint32_t localeId;
if (langTags == nullptr) {
@@ -71,7 +71,8 @@ static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderP
}
std::shared_ptr<minikin::FontFamily> family = minikin::FontFamily::create(
localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
- isCustomFallback, isDefaultFallback);
+ isCustomFallback, isDefaultFallback,
+ static_cast<minikin::VariationFamilyType>(variationFamilyType));
if (family->getCoverage().length() == 0) {
// No coverage means minikin rejected given font for some reasons.
jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -121,7 +122,7 @@ static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
{"nInitBuilder", "()J", (void*)FontFamily_Builder_initBuilder},
{"nAddFont", "(JJ)V", (void*)FontFamily_Builder_addFont},
- {"nBuild", "(JLjava/lang/String;IZZ)J", (void*)FontFamily_Builder_build},
+ {"nBuild", "(JLjava/lang/String;IZZI)J", (void*)FontFamily_Builder_build},
{"nGetReleaseNativeFamily", "()J", (void*)FontFamily_Builder_GetReleaseFunc},
};
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index d69a47c5b085..8c377b9f1829 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -148,6 +148,30 @@ static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i
}
// CriticalNative
+static jboolean TextShaper_Result_getFakeBold(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).isFakeBold();
+}
+
+// CriticalNative
+static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).isFakeItalic();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).wghtAdjustment();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getFakery(i).italAdjustment();
+}
+
+// CriticalNative
static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
@@ -185,15 +209,19 @@ static const JNINativeMethod gMethods[] = {
};
static const JNINativeMethod gResultMethods[] = {
- { "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount },
- { "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance },
- { "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent },
- { "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent },
- { "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId },
- { "nGetX", "(JI)F", (void*)TextShaper_Result_getX },
- { "nGetY", "(JI)F", (void*)TextShaper_Result_getY },
- { "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont },
- { "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc },
+ {"nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount},
+ {"nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance},
+ {"nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent},
+ {"nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent},
+ {"nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId},
+ {"nGetX", "(JI)F", (void*)TextShaper_Result_getX},
+ {"nGetY", "(JI)F", (void*)TextShaper_Result_getY},
+ {"nGetFont", "(JI)J", (void*)TextShaper_Result_getFont},
+ {"nGetFakeBold", "(JI)Z", (void*)TextShaper_Result_getFakeBold},
+ {"nGetFakeItalic", "(JI)Z", (void*)TextShaper_Result_getFakeItalic},
+ {"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride},
+ {"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride},
+ {"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc},
};
int register_android_graphics_text_TextShaper(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 00919dc3f22a..f71e7289bd37 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -79,7 +79,7 @@ bool ShaderCache::validateCache(const void* identity, ssize_t size) {
void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
ATRACE_NAME("initShaderDiskCache");
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
// Emulators can switch between different renders either as part of config
// or snapshot migration. Also, program binaries may not work well on some
@@ -92,7 +92,7 @@ void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
}
void ShaderCache::setFilename(const char* filename) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
mFilename = filename;
}
@@ -104,7 +104,7 @@ BlobCache* ShaderCache::getBlobCacheLocked() {
sk_sp<SkData> ShaderCache::load(const SkData& key) {
ATRACE_NAME("ShaderCache::load");
size_t keySize = key.size();
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
if (!mInitialized) {
return nullptr;
}
@@ -181,13 +181,18 @@ void ShaderCache::saveToDiskLocked() {
auto key = sIDKey;
set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
}
+ // The most straightforward way to make ownership shared
+ mMutex.unlock();
+ mMutex.lock_shared();
mBlobCache->writeToFile();
+ mMutex.unlock_shared();
+ mMutex.lock();
}
}
void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
ATRACE_NAME("ShaderCache::store");
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
mNumShadersCachedInRam++;
ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
@@ -229,7 +234,7 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /
mSavePending = true;
std::thread deferredSaveThread([this]() {
usleep(mDeferredSaveDelayMs * 1000); // milliseconds to microseconds
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
// Store file on disk if there a new shader or Vulkan pipeline cache size changed.
if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
saveToDiskLocked();
@@ -245,11 +250,12 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /
void ShaderCache::onVkFrameFlushed(GrDirectContext* context) {
{
- std::lock_guard<std::mutex> lock(mMutex);
-
+ mMutex.lock_shared();
if (!mInitialized || !mTryToStorePipelineCache) {
+ mMutex.unlock_shared();
return;
}
+ mMutex.unlock_shared();
}
mInStoreVkPipelineInProgress = true;
context->storeVkPipelineCacheData();
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 0492d70652df..c63a65ac1ed8 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -19,8 +19,10 @@
#include <GrContextOptions.h>
#include <SkRefCnt.h>
#include <cutils/compiler.h>
+#include <ftl/shared_mutex.h>
+#include <utils/Mutex.h>
+
#include <memory>
-#include <mutex>
#include <string>
#include <vector>
@@ -100,20 +102,20 @@ private:
* this will do so, loading the serialized cache contents from disk if
* possible.
*/
- BlobCache* getBlobCacheLocked();
+ BlobCache* getBlobCacheLocked() REQUIRES(mMutex);
/**
* "validateCache" updates the cache to match the given identity. If the
* cache currently has the wrong identity, all entries in the cache are cleared.
*/
- bool validateCache(const void* identity, ssize_t size);
+ bool validateCache(const void* identity, ssize_t size) REQUIRES(mMutex);
/**
- * "saveToDiskLocked" attemps to save the current contents of the cache to
+ * "saveToDiskLocked" attempts to save the current contents of the cache to
* disk. If the identity hash exists, we will insert the identity hash into
* the cache for next validation.
*/
- void saveToDiskLocked();
+ void saveToDiskLocked() REQUIRES(mMutex);
/**
* "mInitialized" indicates whether the ShaderCache is in the initialized
@@ -123,7 +125,7 @@ private:
* the load and store methods will return without performing any cache
* operations.
*/
- bool mInitialized = false;
+ bool mInitialized GUARDED_BY(mMutex) = false;
/**
* "mBlobCache" is the cache in which the key/value blob pairs are stored. It
@@ -132,7 +134,7 @@ private:
* The blob cache contains the Android build number. We treat version mismatches as an empty
* cache (logic implemented in BlobCache::unflatten).
*/
- std::unique_ptr<FileBlobCache> mBlobCache;
+ std::unique_ptr<FileBlobCache> mBlobCache GUARDED_BY(mMutex);
/**
* "mFilename" is the name of the file for storing cache contents in between
@@ -141,7 +143,7 @@ private:
* empty string indicates that the cache should not be saved to or restored
* from disk.
*/
- std::string mFilename;
+ std::string mFilename GUARDED_BY(mMutex);
/**
* "mIDHash" is the current identity hash for the cache validation. It is
@@ -150,7 +152,7 @@ private:
* indicates that cache validation is not performed, and the hash should
* not be stored on disk.
*/
- std::vector<uint8_t> mIDHash;
+ std::vector<uint8_t> mIDHash GUARDED_BY(mMutex);
/**
* "mSavePending" indicates whether or not a deferred save operation is
@@ -160,7 +162,7 @@ private:
* contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
* is disabled.
*/
- bool mSavePending = false;
+ bool mSavePending GUARDED_BY(mMutex) = false;
/**
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
@@ -175,16 +177,16 @@ private:
unsigned int mDeferredSaveDelayMs = 4 * 1000;
/**
- * "mMutex" is the mutex used to prevent concurrent access to the member
+ * "mMutex" is the shared mutex used to prevent concurrent access to the member
* variables. It must be locked whenever the member variables are accessed.
*/
- mutable std::mutex mMutex;
+ mutable ftl::SharedMutex mMutex;
/**
* If set to "true", the next call to onVkFrameFlushed, will invoke
* GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
*/
- bool mTryToStorePipelineCache = true;
+ bool mTryToStorePipelineCache GUARDED_BY(mMutex) = true;
/**
* This flag is used by "ShaderCache::store" to distinguish between shader data and
@@ -196,16 +198,16 @@ private:
* "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
* to prevent unnecessary disk writes, if the pipeline cache size has not changed.
*/
- size_t mNewPipelineCacheSize = -1;
+ size_t mNewPipelineCacheSize GUARDED_BY(mMutex) = -1;
/**
* "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
*/
- size_t mOldPipelineCacheSize = -1;
+ size_t mOldPipelineCacheSize GUARDED_BY(mMutex) = -1;
/**
* "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
*/
- bool mCacheDirty = false;
+ bool mCacheDirty GUARDED_BY(mMutex) = false;
/**
* "sCache" is the singleton ShaderCache object.
@@ -222,7 +224,7 @@ private:
* interesting to keep track of how many shaders are stored in RAM. This
* class provides a convenient entry point for that.
*/
- int mNumShadersCachedInRam = 0;
+ int mNumShadersCachedInRam GUARDED_BY(mMutex) = 0;
friend class ShaderCacheTestUtils; // used for unit testing
};
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 7bcd45c6b643..9aa2e1db4461 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -49,7 +49,7 @@ public:
*/
static void reinitializeAllFields(ShaderCache& cache) {
ShaderCache newCache = ShaderCache();
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex), newLock(newCache.mMutex);
// By order of declaration
cache.mInitialized = newCache.mInitialized;
cache.mBlobCache.reset(nullptr);
@@ -72,7 +72,7 @@ public:
* manually, as seen in the "terminate" testing helper function.
*/
static void setSaveDelayMs(ShaderCache& cache, unsigned int saveDelayMs) {
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
cache.mDeferredSaveDelayMs = saveDelayMs;
}
@@ -81,7 +81,7 @@ public:
* Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
*/
static void terminate(ShaderCache& cache, bool saveContent) {
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
if (saveContent) {
cache.saveToDiskLocked();
}
@@ -93,6 +93,7 @@ public:
*/
template <typename T>
static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
+ std::lock_guard lock(cache.mMutex);
return cache.validateCache(hash.data(), hash.size() * sizeof(T));
}
@@ -108,7 +109,7 @@ public:
*/
static void waitForPendingSave(ShaderCache& cache, const int timeoutMs = 50) {
{
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
ASSERT_TRUE(cache.mSavePending);
}
bool saving = true;
@@ -123,7 +124,7 @@ public:
usleep(delayMicroseconds);
elapsedMilliseconds += (float)delayMicroseconds / 1000;
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
saving = cache.mSavePending;
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3123ee6dd4d7..3f013de8dad6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1342,6 +1342,11 @@ public class AudioSystem
}
/** @hide */
+ public static boolean isRemoteSubmixDevice(int deviceType) {
+ return deviceType == DEVICE_IN_REMOTE_SUBMIX || deviceType == DEVICE_OUT_REMOTE_SUBMIX;
+ }
+
+ /** @hide */
public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0";
// device states, must match AudioSystem::device_connection_state
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index ed68d1eb5724..d0270d3d3246 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -16,6 +16,9 @@
package android.media.audiopolicy;
+import static android.media.AudioSystem.getDeviceName;
+import static android.media.AudioSystem.isRemoteSubmixDevice;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -445,36 +448,36 @@ public class AudioMix {
// CHANNEL_IN_FRONT_BACK is hidden, should not appear.
}
}
- if ((mDeviceSystemType != AudioSystem.DEVICE_NONE)
- && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)
- && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) {
- if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) {
- throw new IllegalArgumentException(
- "Can't have audio device without flag ROUTE_FLAG_RENDER");
- }
- if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
- throw new IllegalArgumentException("Unsupported device on non-playback mix");
+
+ if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) {
+ if (mDeviceSystemType == AudioSystem.DEVICE_NONE) {
+ // If there was no device type explicitly set, configure it based on mix type.
+ mDeviceSystemType = getLoopbackDeviceSystemTypeForAudioMixingRule(mRule);
+ } else if (!isRemoteSubmixDevice(mDeviceSystemType)) {
+ // Loopback mode only supports remote submix devices.
+ throw new IllegalArgumentException("Device " + getDeviceName(mDeviceSystemType)
+ + "is not supported for loopback mix.");
}
- } else if (mDeviceSystemType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) {
- if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
+ }
+
+ if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) {
+ if (mDeviceSystemType == AudioSystem.DEVICE_NONE) {
throw new IllegalArgumentException(
- "DEVICE_OUT_REMOTE_SUBMIX device is not supported on non-playback mix");
+ "Can't have flag ROUTE_FLAG_RENDER without an audio device");
}
- } else {
- if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) {
+
+ if (AudioSystem.DEVICE_IN_ALL_SET.contains(mDeviceSystemType)) {
throw new IllegalArgumentException(
- "Can't have flag ROUTE_FLAG_RENDER without an audio device");
+ "Input device is not supported with ROUTE_FLAG_RENDER");
}
- if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) {
- if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) {
- mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
- } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
- mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX;
- } else {
- throw new IllegalArgumentException("Unknown mixing rule type");
- }
+
+ if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
+ throw new IllegalArgumentException(
+ "ROUTE_FLAG_RENDER/ROUTE_FLAG_LOOP_BACK_RENDER is not supported for "
+ + "non-playback mix rule");
}
}
+
if (mRule.allowPrivilegedMediaPlaybackCapture()) {
String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat);
if (error != null) {
@@ -484,5 +487,18 @@ public class AudioMix {
return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
mDeviceAddress);
}
+
+ private int getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule) {
+ switch (mRule.getTargetMixType()) {
+ case MIX_TYPE_PLAYERS:
+ return AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ case MIX_TYPE_RECORDERS:
+ return AudioSystem.DEVICE_IN_REMOTE_SUBMIX;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown mixing rule type - 0x" + Integer.toHexString(
+ rule.getTargetMixType()));
+ }
+ }
}
}
diff --git a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
index 2231ce14eea6..e46d34e81483 100644
--- a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
+++ b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
@@ -17,9 +17,22 @@
package android.media.projection;
import android.media.projection.MediaProjectionInfo;
+import android.view.ContentRecordingSession;
/** {@hide} */
oneway interface IMediaProjectionWatcherCallback {
void onStart(in MediaProjectionInfo info);
void onStop(in MediaProjectionInfo info);
+ /**
+ * Called when the {@link ContentRecordingSession} was set for the current media
+ * projection.
+ *
+ * @param info always present and contains information about the media projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ void onRecordingSessionSet(
+ in MediaProjectionInfo info,
+ in @nullable ContentRecordingSession session
+ );
}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 5703c429c32b..5a68c53b8f68 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -29,6 +29,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.ContentRecordingSession;
import android.view.Surface;
import java.util.Map;
@@ -300,7 +301,22 @@ public final class MediaProjectionManager {
/** @hide */
public static abstract class Callback {
public abstract void onStart(MediaProjectionInfo info);
+
public abstract void onStop(MediaProjectionInfo info);
+
+ /**
+ * Called when the {@link ContentRecordingSession} was set for the current media
+ * projection.
+ *
+ * @param info always present and contains information about the media projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ public void onRecordingSessionSet(
+ @NonNull MediaProjectionInfo info,
+ @Nullable ContentRecordingSession session
+ ) {
+ }
}
/** @hide */
@@ -335,5 +351,13 @@ public final class MediaProjectionManager {
}
});
}
+
+ @Override
+ public void onRecordingSessionSet(
+ @NonNull final MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session
+ ) {
+ mHandler.post(() -> mCallback.onRecordingSessionSet(info, session));
+ }
}
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index af33149867bd..f664fdc949de 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -267,25 +267,33 @@ public final class MediaSession {
}
/**
- * Set a pending intent for your media button receiver to allow restarting
- * playback after the session has been stopped. If your app is started in
- * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
- * the pending intent.
- * <p>
- * The pending intent is recommended to be explicit to follow the security recommendation of
- * {@link PendingIntent#getActivity}.
+ * Set a pending intent for your media button receiver to allow restarting playback after the
+ * session has been stopped.
*
- * @param mbr The {@link PendingIntent} to send the media button event to.
- * @see PendingIntent#getActivity
+ * <p>If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be
+ * sent via the pending intent.
+ *
+ * <p>The provided {@link PendingIntent} must not target an activity. On apps targeting Android
+ * V and above, passing an activity pending intent to this method causes an {@link
+ * IllegalArgumentException}. On apps targeting Android U and below, passing an activity pending
+ * intent causes the call to be ignored. Refer to this <a
+ * href="https://developer.android.com/guide/components/activities/background-starts">guide</a>
+ * for more information.
*
+ * <p>The pending intent is recommended to be explicit to follow the security recommendation of
+ * {@link PendingIntent#getService}.
+ *
+ * @param mbr The {@link PendingIntent} to send the media button event to.
* @deprecated Use {@link #setMediaButtonBroadcastReceiver(ComponentName)} instead.
+ * @throws IllegalArgumentException if the pending intent targets an activity on apps targeting
+ * Android V and above.
*/
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+ e.rethrowFromSystemServer();
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 5a569456b4a7..d81843d1323a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1565,6 +1565,10 @@ public class Tuner implements AutoCloseable {
mFrontendCiCamLock.lock();
mFrontendLock.lock();
try {
+ if (mFrontendHandle == null) {
+ Log.d(TAG, "Operation cannot be done without frontend");
+ return RESULT_INVALID_STATE;
+ }
if (mFeOwnerTuner != null) {
Log.d(TAG, "Operation cannot be done by sharee of tuner");
return RESULT_INVALID_STATE;
@@ -1632,6 +1636,10 @@ public class Tuner implements AutoCloseable {
public int disconnectFrontendToCiCam(int ciCamId) {
acquireTRMSLock("disconnectFrontendToCiCam()");
try {
+ if (mFrontendHandle == null) {
+ Log.d(TAG, "Operation cannot be done without frontend");
+ return RESULT_INVALID_STATE;
+ }
if (mFeOwnerTuner != null) {
Log.d(TAG, "Operation cannot be done by sharee of tuner");
return RESULT_INVALID_STATE;
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
index ac8a7f37c527..a26398ac198a 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
@@ -117,7 +117,7 @@ public class AudioMixUnitTests {
// --- Equality group 3
final AudioMix recordingAudioMixWithSessionId42AndUid123Render =
new AudioMix.Builder(new AudioMixingRule.Builder()
- .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
.addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
.addMixRule(RULE_MATCH_UID, 123).build())
.setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
@@ -205,7 +205,19 @@ public class AudioMixUnitTests {
}
@Test
- public void buildLoopbackWithDevice_throws() {
+ public void buildLoopbackForInjectorMix_success() {
+ final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .addMixRule(RULE_MATCH_UID, 42).build())
+ .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+
+ assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
+ assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags());
+ }
+
+ @Test
+ public void buildLoopbackWithIncompatibleDevice_throws() {
assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
new AudioMixingRule.Builder()
.setTargetMixRole(MIX_ROLE_PLAYERS)
@@ -225,6 +237,28 @@ public class AudioMixUnitTests {
.setRouteFlags(AudioMix.ROUTE_FLAG_RENDER).build());
}
+ @Test
+ public void buildRenderWithInputDevice_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
+ new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
+ .addMixRule(RULE_MATCH_UID, 42).build())
+ .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
+ .setDevice(AudioSystem.DEVICE_IN_BUILTIN_MIC, /*address=*/"").build());
+ }
+
+ @Test
+ public void buildRenderWithInjectorMix_throws() {
+ assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
+ new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .addMixRule(RULE_MATCH_UID, 42).build())
+ .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
+ .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build());
+ }
+
private static AudioMix writeToAndFromParcel(AudioMix audioMix) {
diff --git a/packages/CarrierDefaultApp/assets/slice_purchase_test.html b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
index 917276b9ce65..ad18a9d64074 100644
--- a/packages/CarrierDefaultApp/assets/slice_purchase_test.html
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
@@ -81,5 +81,7 @@
Dismiss flow
</button>
<p id="dismiss_flow"></p>
+
+ <h2>Test <a href="http://www.google.com">hyperlink</a></h2>
</body>
</html>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
index 2530257d629a..b1009808cccc 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
@@ -29,6 +29,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.webkit.CookieManager;
import android.webkit.WebView;
+import android.webkit.WebViewClient;
import com.android.phone.slice.SlicePurchaseController;
@@ -113,8 +114,10 @@ public class SlicePurchaseActivity extends Activity {
return;
}
- // Create and configure WebView
- setupWebView();
+ // Clear any cookies that might be persisted from previous sessions before loading WebView
+ CookieManager.getInstance().removeAllCookies(value -> {
+ setupWebView();
+ });
}
protected void onPurchaseSuccessful() {
@@ -176,12 +179,7 @@ public class SlicePurchaseActivity extends Activity {
private void setupWebView() {
// Create WebView
mWebView = new WebView(this);
-
- // Clear any cookies and state that might be saved from previous sessions
- CookieManager.getInstance().removeAllCookies(null);
- CookieManager.getInstance().flush();
- mWebView.clearCache(true);
- mWebView.clearHistory();
+ mWebView.setWebViewClient(new WebViewClient());
// Enable JavaScript for the carrier purchase website to send results back to
// the slice purchase application.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 1231bb3fa416..f2f019d96023 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -269,9 +269,16 @@ public class InstallStart extends Activity {
}
private boolean isCallerSessionOwner(int originatingUid, int sessionId) {
+ if (originatingUid == Process.ROOT_UID) {
+ return true;
+ }
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
- int installerUid = packageInstaller.getSessionInfo(sessionId).getInstallerUid();
- return (originatingUid == Process.ROOT_UID) || (originatingUid == installerUid);
+ PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
+ if (sessionInfo == null) {
+ return false;
+ }
+ int installerUid = sessionInfo.getInstallerUid();
+ return originatingUid == installerUid;
}
private void checkDevicePolicyRestriction() {
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index 2f30508ca504..3586dcb2909c 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -33,13 +33,13 @@
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/dialog_with_icon_title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
style="@style/DialogWithIconTitle"/>
<TextView
android:id="@+id/dialog_with_icon_message"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
style="@style/TextAppearanceSmall"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 00ccea1f93f1..ff960f3184db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -409,7 +409,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
*/
public static EnforcedAdmin checkIfUsbDataSignalingIsDisabled(Context context, int userId) {
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
- if (dpm == null || dpm.isUsbDataSignalingEnabledForUser(userId)) {
+ if (dpm == null || dpm.isUsbDataSignalingEnabled()) {
return null;
} else {
EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId));
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index b5e4fa38d244..af06d7304160 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -243,7 +243,9 @@ public class RestrictedSwitchPreference extends SwitchPreference {
return mHelper != null ? mHelper.packageName : null;
}
- public void updateState(@NonNull String packageName, int uid, boolean isEnabled) {
+ /** Updates enabled state based on associated package. */
+ public void updateState(
+ @NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
mHelper.updatePackageDetails(packageName, uid);
if (mAppOpsManager == null) {
mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
@@ -254,7 +256,9 @@ public class RestrictedSwitchPreference extends SwitchPreference {
final boolean ecmEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- if (isEnabled) {
+ if (!isEnableAllowed && !isEnabled) {
+ setEnabled(false);
+ } else if (isEnabled) {
setEnabled(true);
} else if (appOpsAllowed && isDisabledByAppOps()) {
setEnabled(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 2e6bb535a8f0..f522fd13c9f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -583,7 +583,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
*/
public void setName(String name) {
// Prevent getName() to be set to null if setName(null) is called
- if (name == null || TextUtils.equals(name, getName())) {
+ if (TextUtils.isEmpty(name) || TextUtils.equals(name, getName())) {
return;
}
mDevice.setAlias(name);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index cd6609ec463e..963bd9daa975 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.media;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
@@ -22,6 +24,7 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -39,7 +42,13 @@ public class BluetoothMediaDevice extends MediaDevice {
BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, device, routerManager, info, packageName, null);
+ }
+
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+ MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName,
+ RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mCachedDevice = device;
mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
@@ -58,6 +67,12 @@ public class BluetoothMediaDevice extends MediaDevice {
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public Drawable getIcon() {
return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice())
? mContext.getDrawable(R.drawable.ic_earbuds_advanced)
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a1beed8569d9..3e864f905da3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -50,7 +50,6 @@ import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.media.MediaRoute2Info;
-import android.media.MediaRouter2Manager;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.os.Build;
@@ -71,6 +70,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
/** InfoMediaManager provide interface to get InfoMediaDevice list. */
@RequiresApi(Build.VERSION_CODES.R)
@@ -174,11 +174,34 @@ public abstract class InfoMediaManager extends MediaManager {
MediaRoute2Info route, RouteListingPreference.Item routeListingPreferenceItem);
@NonNull
- protected abstract PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route);
+ protected abstract PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route,
+ RouteListingPreference.Item routeListingPreferenceItem);
@NonNull
protected abstract BluetoothMediaDevice createBluetoothMediaDevice(
- MediaRoute2Info route, CachedBluetoothDevice cachedDevice);
+ MediaRoute2Info route, CachedBluetoothDevice cachedDevice,
+ RouteListingPreference.Item routeListingPreferenceItem);
+
+ protected final void rebuildDeviceList() {
+ mMediaDevices.clear();
+ mCurrentConnectedDevice = null;
+ if (TextUtils.isEmpty(mPackageName)) {
+ buildAllRoutes();
+ } else {
+ buildAvailableRoutes();
+ }
+ }
+
+ protected final void notifyCurrentConnectedDeviceChanged() {
+ final String id = mCurrentConnectedDevice != null ? mCurrentConnectedDevice.getId() : null;
+ dispatchConnectedDeviceChanged(id);
+ }
+
+ @RequiresApi(34)
+ protected final void notifyRouteListingPreferenceUpdated(
+ RouteListingPreference routeListingPreference) {
+ Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
+ }
/**
* Get current device that played media.
@@ -474,14 +497,8 @@ public abstract class InfoMediaManager extends MediaManager {
|| sessionInfo.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
}
- private synchronized void refreshDevices() {
- mMediaDevices.clear();
- mCurrentConnectedDevice = null;
- if (TextUtils.isEmpty(mPackageName)) {
- buildAllRoutes();
- } else {
- buildAvailableRoutes();
- }
+ protected final synchronized void refreshDevices() {
+ rebuildDeviceList();
dispatchDeviceListAdded();
}
@@ -578,7 +595,8 @@ public abstract class InfoMediaManager extends MediaManager {
case TYPE_HDMI:
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
- mediaDevice = createPhoneMediaDevice(route);
+ mediaDevice = createPhoneMediaDevice(route,
+ mPreferenceItemMap.getOrDefault(route.getId(), null));
break;
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
@@ -588,7 +606,8 @@ public abstract class InfoMediaManager extends MediaManager {
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
- mediaDevice = createBluetoothMediaDevice(route, cachedDevice);
+ mediaDevice = createBluetoothMediaDevice(route, cachedDevice,
+ mPreferenceItemMap.getOrDefault(route.getId(), null));
}
break;
case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
@@ -612,75 +631,6 @@ public abstract class InfoMediaManager extends MediaManager {
}
}
- class RouterManagerCallback implements MediaRouter2Manager.Callback {
-
- @Override
- public void onRoutesUpdated() {
- refreshDevices();
- }
-
- @Override
- public void onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures) {
- if (TextUtils.equals(mPackageName, packageName)) {
- refreshDevices();
- }
- }
-
- @Override
- public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) {
- if (DEBUG) {
- Log.d(TAG, "onTransferred() oldSession : " + oldSession.getName()
- + ", newSession : " + newSession.getName());
- }
- mMediaDevices.clear();
- mCurrentConnectedDevice = null;
- if (TextUtils.isEmpty(mPackageName)) {
- buildAllRoutes();
- } else {
- buildAvailableRoutes();
- }
-
- final String id = mCurrentConnectedDevice != null
- ? mCurrentConnectedDevice.getId()
- : null;
- dispatchConnectedDeviceChanged(id);
- }
-
- /**
- * Ignore callback here since we'll also receive{@link
- * MediaRouter2Manager.Callback#onRequestFailed onRequestFailed} with reason code.
- */
- @Override
- public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
- }
-
- @Override
- public void onRequestFailed(int reason) {
- dispatchOnRequestFailed(reason);
- }
-
- @Override
- public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
- refreshDevices();
- }
-
- @Override
- public void onSessionReleased(@NonNull RoutingSessionInfo session) {
- refreshDevices();
- }
-
- @Override
- public void onRouteListingPreferenceUpdated(
- String packageName,
- RouteListingPreference routeListingPreference) {
- if (TextUtils.equals(mPackageName, packageName)) {
- Api34Impl.onRouteListingPreferenceUpdated(
- routeListingPreference, mPreferenceItemMap);
- refreshDevices();
- }
- }
- }
-
@RequiresApi(34)
static class Api34Impl {
@DoNotInline
@@ -719,20 +669,19 @@ public abstract class InfoMediaManager extends MediaManager {
List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist,
List<RouteListingPreference.Item> preferenceRouteListing) {
final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos);
+ infolist.removeAll(selectedRouteInfos);
+ sortedInfoList.addAll(infolist.stream().filter(
+ MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
for (RouteListingPreference.Item item : preferenceRouteListing) {
for (MediaRoute2Info info : infolist) {
if (item.getRouteId().equals(info.getId())
- && !selectedRouteInfos.contains(info)) {
+ && !selectedRouteInfos.contains(info)
+ && !info.isSystemRoute()) {
sortedInfoList.add(info);
break;
}
}
}
- if (sortedInfoList.size() != infolist.size()) {
- infolist.removeAll(sortedInfoList);
- sortedInfoList.addAll(infolist.stream().filter(
- MediaRoute2Info::isSystemRoute).toList());
- }
return sortedInfoList;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 4f081369f5bf..c86a943eff18 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -24,6 +24,8 @@ import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
+import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -39,10 +41,14 @@ import java.util.concurrent.Executors;
*/
public class ManagerInfoMediaManager extends InfoMediaManager {
+ private static final String TAG = "ManagerInfoMediaManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
@VisibleForTesting
/* package */ final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
@VisibleForTesting
/* package */ MediaRouter2Manager mRouterManager;
+ boolean mIsScanning = false;
private final Executor mExecutor = Executors.newSingleThreadExecutor();
@@ -58,14 +64,20 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
@Override
protected void startScanOnRouter() {
- mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
- mRouterManager.registerScanRequest();
+ if (!mIsScanning) {
+ mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ mRouterManager.registerScanRequest();
+ mIsScanning = true;
+ }
}
@Override
public void stopScan() {
- mRouterManager.unregisterCallback(mMediaRouterCallback);
- mRouterManager.unregisterScanRequest();
+ if (mIsScanning) {
+ mRouterManager.unregisterCallback(mMediaRouterCallback);
+ mRouterManager.unregisterScanRequest();
+ mIsScanning = false;
+ }
}
@Override
@@ -173,15 +185,81 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
@Override
@NonNull
- protected PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route) {
- return new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+ protected PhoneMediaDevice createPhoneMediaDevice(MediaRoute2Info route,
+ RouteListingPreference.Item routeListingPreferenceItem) {
+ return new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName,
+ routeListingPreferenceItem);
}
@Override
@NonNull
protected BluetoothMediaDevice createBluetoothMediaDevice(
- MediaRoute2Info route, CachedBluetoothDevice cachedDevice) {
+ MediaRoute2Info route, CachedBluetoothDevice cachedDevice,
+ RouteListingPreference.Item routeListingPreferenceItem) {
return new BluetoothMediaDevice(
- mContext, cachedDevice, mRouterManager, route, mPackageName);
+ mContext, cachedDevice, mRouterManager, route, mPackageName,
+ routeListingPreferenceItem);
+ }
+
+ @VisibleForTesting
+ /* package */ final class RouterManagerCallback implements MediaRouter2Manager.Callback {
+
+ @Override
+ public void onRoutesUpdated() {
+ refreshDevices();
+ }
+
+ @Override
+ public void onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures) {
+ if (TextUtils.equals(mPackageName, packageName)) {
+ refreshDevices();
+ }
+ }
+
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onTransferred() oldSession : "
+ + oldSession.getName()
+ + ", newSession : "
+ + newSession.getName());
+ }
+ rebuildDeviceList();
+ notifyCurrentConnectedDeviceChanged();
+ }
+
+ /**
+ * Ignore callback here since we'll also receive{@link
+ * MediaRouter2Manager.Callback#onRequestFailed onRequestFailed} with reason code.
+ */
+ @Override
+ public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {}
+
+ @Override
+ public void onRequestFailed(int reason) {
+ dispatchOnRequestFailed(reason);
+ }
+
+ @Override
+ public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
+ refreshDevices();
+ }
+
+ @Override
+ public void onSessionReleased(@NonNull RoutingSessionInfo session) {
+ refreshDevices();
+ }
+
+ @Override
+ public void onRouteListingPreferenceUpdated(
+ String packageName, RouteListingPreference routeListingPreference) {
+ if (!TextUtils.equals(mPackageName, packageName)) {
+ return;
+ }
+ notifyRouteListingPreferenceUpdated(routeListingPreference);
+ refreshDevices();
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 34519c993d27..accd88c2bfe3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -24,10 +24,13 @@ import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import androidx.annotation.VisibleForTesting;
@@ -51,7 +54,12 @@ public class PhoneMediaDevice extends MediaDevice {
PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, routerManager, info, packageName, null);
+ }
+
+ PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName, RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mDeviceIconUtil = new DeviceIconUtil();
initDeviceRecord();
}
@@ -86,6 +94,12 @@ public class PhoneMediaDevice extends MediaDevice {
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public String getSummary() {
return mSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
index c40388fee710..361a24612b3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoEntity.java
@@ -16,27 +16,23 @@
package com.android.settingslib.mobile.dataservice;
-import static androidx.room.ForeignKey.CASCADE;
-
import android.text.TextUtils;
-import java.util.Objects;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
-import androidx.room.ForeignKey;
-import androidx.room.Index;
import androidx.room.PrimaryKey;
+import java.util.Objects;
+
@Entity(tableName = DataServiceUtils.SubscriptionInfoData.TABLE_NAME)
public class SubscriptionInfoEntity {
public SubscriptionInfoEntity(@NonNull String subId, int simSlotIndex, int carrierId,
String displayName, String carrierName, int dataRoaming, String mcc, String mnc,
String countryIso, boolean isEmbedded, int cardId, int portIndex,
boolean isOpportunistic, @Nullable String groupUUID, int subscriptionType,
- String uniqueName, boolean isSubscriptionVisible, String formattedPhoneNumber,
+ String uniqueName, boolean isSubscriptionVisible, @Nullable String formattedPhoneNumber,
boolean isFirstRemovableSubscription, boolean isDefaultSubscriptionSelection,
boolean isValidSubscription, boolean isUsableSubscription,
boolean isActiveSubscriptionId, boolean isAvailableSubscription,
@@ -123,6 +119,7 @@ public class SubscriptionInfoEntity {
public boolean isSubscriptionVisible;
@ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_FORMATTED_PHONE_NUMBER)
+ @Nullable
public String formattedPhoneNumber;
@ColumnInfo(name = DataServiceUtils.SubscriptionInfoData.COLUMN_IS_FIRST_REMOVABLE_SUBSCRIPTION)
@@ -165,33 +162,32 @@ public class SubscriptionInfoEntity {
@Override
public int hashCode() {
- int result = 17;
- result = 31 * result + subId.hashCode();
- result = 31 * result + simSlotIndex;
- result = 31 * result + carrierId;
- result = 31 * result + displayName.hashCode();
- result = 31 * result + carrierName.hashCode();
- result = 31 * result + dataRoaming;
- result = 31 * result + mcc.hashCode();
- result = 31 * result + mnc.hashCode();
- result = 31 * result + countryIso.hashCode();
- result = 31 * result + Boolean.hashCode(isEmbedded);
- result = 31 * result + cardId;
- result = 31 * result + portIndex;
- result = 31 * result + Boolean.hashCode(isOpportunistic);
- result = 31 * result + groupUUID.hashCode();
- result = 31 * result + subscriptionType;
- result = 31 * result + uniqueName.hashCode();
- result = 31 * result + Boolean.hashCode(isSubscriptionVisible);
- result = 31 * result + formattedPhoneNumber.hashCode();
- result = 31 * result + Boolean.hashCode(isFirstRemovableSubscription);
- result = 31 * result + Boolean.hashCode(isDefaultSubscriptionSelection);
- result = 31 * result + Boolean.hashCode(isValidSubscription);
- result = 31 * result + Boolean.hashCode(isUsableSubscription);
- result = 31 * result + Boolean.hashCode(isActiveSubscriptionId);
- result = 31 * result + Boolean.hashCode(isAvailableSubscription);
- result = 31 * result + Boolean.hashCode(isActiveDataSubscriptionId);
- return result;
+ return Objects.hash(
+ subId,
+ simSlotIndex,
+ carrierId,
+ displayName,
+ carrierName,
+ dataRoaming,
+ mcc,
+ mnc,
+ countryIso,
+ isEmbedded,
+ cardId,
+ portIndex,
+ isOpportunistic,
+ groupUUID,
+ subscriptionType,
+ uniqueName,
+ isSubscriptionVisible,
+ formattedPhoneNumber,
+ isFirstRemovableSubscription,
+ isDefaultSubscriptionSelection,
+ isValidSubscription,
+ isUsableSubscription,
+ isActiveSubscriptionId,
+ isAvailableSubscription,
+ isActiveDataSubscriptionId);
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 6444f3bd4341..4b61ff1177bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1015,6 +1015,13 @@ public class CachedBluetoothDeviceTest {
}
@Test
+ public void setName_setDeviceNameIsEmpty() {
+ mCachedDevice.setName("");
+
+ verify(mDevice, never()).setAlias(any());
+ }
+
+ @Test
public void getProfileConnectionState_nullProfile_returnDisconnected() {
assertThat(mCachedDevice.getProfileConnectionState(null)).isEqualTo(
BluetoothProfile.STATE_DISCONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index 5f53a92c131e..35223c28613f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -140,8 +140,8 @@ public class EnableAdbPreferenceControllerTest {
public void updateState_settingsOn_shouldCheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
- when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
- UserHandle.myUserId())).thenReturn(true);
+ when(mDevicePolicyManager.isUsbDataSignalingEnabled(
+ )).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 1);
mPreference.setChecked(false);
@@ -156,8 +156,8 @@ public class EnableAdbPreferenceControllerTest {
public void updateState_settingsOff_shouldUncheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
- when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
- UserHandle.myUserId())).thenReturn(true);
+ when(mDevicePolicyManager.isUsbDataSignalingEnabled(
+ )).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 0);
mPreference.setChecked(true);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index ce1744dd415e..ee68fc2fa00d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -115,6 +115,23 @@ public class InfoMediaManagerTest {
}
@Test
+ public void stopScan_notStartFirst_notCallsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager, never()).unregisterScanRequest();
+ }
+
+ @Test
+ public void stopScan_startFirst_callsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.startScan();
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager).unregisterScanRequest();
+ }
+
+ @Test
public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -328,11 +345,12 @@ public class InfoMediaManagerTest {
routeListingPreference);
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(4);
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue();
- assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
+ assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_1);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_4);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).isSuggestedDevice()).isTrue();
+ assertThat(mInfoMediaManager.mMediaDevices.get(3).getId()).isEqualTo(TEST_ID_3);
}
@Test
@@ -406,8 +424,13 @@ public class InfoMediaManagerTest {
when(availableInfo3.getClientPackageName()).thenReturn(packageName);
availableRoutes.add(availableInfo3);
- when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(
- availableRoutes);
+ final MediaRoute2Info availableInfo4 = mock(MediaRoute2Info.class);
+ when(availableInfo4.getId()).thenReturn(TEST_ID_1);
+ when(availableInfo4.isSystemRoute()).thenReturn(true);
+ when(availableInfo4.getClientPackageName()).thenReturn(packageName);
+ availableRoutes.add(availableInfo4);
+
+ when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(availableRoutes);
return availableRoutes;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index c058a61a3e9e..f22e090fe7df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -19,6 +19,9 @@ import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
+
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +35,7 @@ import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.NearbyDevice;
+import android.media.RouteListingPreference;
import android.os.Parcel;
import com.android.settingslib.bluetooth.A2dpProfile;
@@ -110,6 +114,8 @@ public class MediaDeviceTest {
@Mock
private MediaRouter2Manager mMediaRouter2Manager;
+ private RouteListingPreference.Item mItem;
+
private BluetoothMediaDevice mBluetoothMediaDevice1;
private BluetoothMediaDevice mBluetoothMediaDevice2;
private BluetoothMediaDevice mBluetoothMediaDevice3;
@@ -497,4 +503,21 @@ public class MediaDeviceTest {
assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
}
+
+ @Test
+ public void getSelectionBehavior_setItemWithSelectionBehaviorOnSystemRoute_returnTransfer() {
+ mItem = new RouteListingPreference.Item.Builder(DEVICE_ADDRESS_1)
+ .setSelectionBehavior(SELECTION_BEHAVIOR_GO_TO_APP)
+ .build();
+ mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1,
+ mMediaRouter2Manager, null /* MediaRoute2Info */, TEST_PACKAGE_NAME, mItem);
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+ TEST_PACKAGE_NAME, mItem);
+
+ assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ assertThat(mPhoneMediaDevice.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9c70a7073293..1784e4840151 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1261,11 +1261,13 @@ public class SettingsProvider extends ContentProvider {
Setting settingLocked = mSettingsRegistry.getSettingLocked(
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
Global.DEVICE_CONFIG_SYNC_DISABLED);
- if (settingLocked == null) {
- return SYNC_DISABLED_MODE_NONE;
+ String settingValue = settingLocked == null ? null : settingLocked.getValue();
+ if (settingValue == null) {
+ // Disable sync by default in test harness mode.
+ return ActivityManager.isRunningInUserTestHarness()
+ ? SYNC_DISABLED_MODE_PERSISTENT : SYNC_DISABLED_MODE_NONE;
}
- String settingValue = settingLocked.getValue();
- boolean isSyncDisabledPersistent = settingValue != null && !"0".equals(settingValue);
+ boolean isSyncDisabledPersistent = !"0".equals(settingValue);
return isSyncDisabledPersistent
? SYNC_DISABLED_MODE_PERSISTENT : SYNC_DISABLED_MODE_NONE;
} finally {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 140b1e02fc14..016dc2144d3f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -346,6 +346,8 @@ public class SettingsBackupTest {
Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
Settings.Global.MOBILE_DATA, // Candidate for backup?
Settings.Global.MOBILE_DATA_ALWAYS_ON,
+ Settings.Global.DSRM_DURATION_MILLIS,
+ Settings.Global.DSRM_ENABLED_ACTIONS,
Settings.Global.MODE_RINGER,
Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
Settings.Global.MULTI_SIM_SMS_PROMPT,
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
index c8999fbcd271..1ac9bbbd9f18 100644
--- a/packages/SoundPicker/Android.bp
+++ b/packages/SoundPicker/Android.bp
@@ -19,6 +19,10 @@ android_library {
"androidx.appcompat_appcompat",
"hilt_android",
"guava",
+ "androidx.recyclerview_recyclerview",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.viewpager2_viewpager2",
+ "com.google.android.material_material",
],
}
diff --git a/packages/SoundPicker/AndroidManifest.xml b/packages/SoundPicker/AndroidManifest.xml
index 1f99e75ebc88..934b003c605c 100644
--- a/packages/SoundPicker/AndroidManifest.xml
+++ b/packages/SoundPicker/AndroidManifest.xml
@@ -34,6 +34,9 @@
<intent-filter>
<action android:name="android.intent.action.RINGTONE_PICKER" />
<category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" />
</intent-filter>
</activity>
</application>
diff --git a/packages/SoundPicker/res/layout/activity_ringtone_picker.xml b/packages/SoundPicker/res/layout/activity_ringtone_picker.xml
index 4eecf89bb481..6fc60801ad3a 100644
--- a/packages/SoundPicker/res/layout/activity_ringtone_picker.xml
+++ b/packages/SoundPicker/res/layout/activity_ringtone_picker.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +16,6 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" /> \ No newline at end of file
+ android:layout_height="match_parent"/> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/add_new_sound_item.xml b/packages/SoundPicker/res/layout/add_new_sound_item.xml
index 14421c9a50dc..024b97ef23be 100644
--- a/packages/SoundPicker/res/layout/add_new_sound_item.xml
+++ b/packages/SoundPicker/res/layout/add_new_sound_item.xml
@@ -16,12 +16,14 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground">
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
+ android:clickable="true">
-<ImageView
+ <ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
@@ -29,9 +31,9 @@
android:scaleType="centerCrop"
android:layout_marginRight="24dp"
android:layout_marginLeft="24dp"
- android:src="@drawable/ic_add" />
+ android:src="@drawable/ic_add"/>
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ <TextView
android:id="@+id/add_new_sound_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -43,5 +45,5 @@
android:gravity="center_vertical"
android:paddingEnd="?android:attr/dialogPreferredPadding"
android:drawablePadding="20dp"
- android:ellipsize="marquee" />
+ android:ellipsize="marquee"/>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/fragment_sound_picker.xml b/packages/SoundPicker/res/layout/fragment_sound_picker.xml
new file mode 100644
index 000000000000..787f92ec06d6
--- /dev/null
+++ b/packages/SoundPicker/res/layout/fragment_sound_picker.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<androidx.recyclerview.widget.RecyclerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+/> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml b/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml
new file mode 100644
index 000000000000..7efd91191b79
--- /dev/null
+++ b/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.google.android.material.tabs.TabLayout
+ android:id="@+id/tabLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <androidx.viewpager2.widget.ViewPager2
+ android:id="@+id/masterViewPager"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/fragment_vibration_picker.xml b/packages/SoundPicker/res/layout/fragment_vibration_picker.xml
new file mode 100644
index 000000000000..34d95aa2e81b
--- /dev/null
+++ b/packages/SoundPicker/res/layout/fragment_vibration_picker.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/empty_list"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center"
+ />
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
index c8ca231f27a4..36ac93ed630b 100644
--- a/packages/SoundPicker/res/layout/radio_with_work_badge.xml
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -14,12 +14,14 @@
limitations under the License.
-->
-<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.soundpicker.CheckedListItem
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
- >
+ android:focusable="true"
+ android:clickable="true">
<CheckedTextView
android:id="@+id/checked_text_view"
@@ -35,7 +37,7 @@
android:drawablePadding="20dp"
android:ellipsize="marquee"
android:layout_toLeftOf="@+id/work_icon"
- android:maxLines="3" />
+ android:maxLines="3"/>
<ImageView
android:id="@id/work_icon"
@@ -44,5 +46,5 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
- android:layout_marginRight="20dp" />
+ android:layout_marginRight="20dp"/>
</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/values/strings.xml b/packages/SoundPicker/res/values/strings.xml
index 04a2c2bb83c3..ab7b95a09028 100644
--- a/packages/SoundPicker/res/values/strings.xml
+++ b/packages/SoundPicker/res/values/strings.xml
@@ -40,4 +40,8 @@
<!-- Text for the name of the app. [CHAR LIMIT=12] -->
<string name="app_label">Sounds</string>
+
+ <string name="empty_list">The list is empty</string>
+ <string name="sound_page_title">Sound</string>
+ <string name="vibration_page_title">Vibration</string>
</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java b/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java
new file mode 100644
index 000000000000..83d04a345f8b
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.annotation.Nullable;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * A cursor wrapper class mainly used to guarantee getting a ringtone title
+ */
+final class LocalizedCursor extends CursorWrapper {
+
+ private static final String TAG = "LocalizedCursor";
+ private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
+
+ private final int mTitleIndex;
+ private final Resources mResources;
+ private final Pattern mSanitizePattern;
+ private final String mNamePrefix;
+
+ LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
+ super(cursor);
+ mTitleIndex = mCursor.getColumnIndex(columnLabel);
+ mResources = resources;
+ mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
+ if (mTitleIndex == -1) {
+ Log.e(TAG, "No index for column " + columnLabel);
+ mNamePrefix = null;
+ } else {
+ mNamePrefix = buildNamePrefix(mResources);
+ }
+ }
+
+ /**
+ * Builds the prefix for the name of the resource to look up.
+ * The format is: "ResourcePackageName::ResourceTypeName/" (the type name is expected to be
+ * "string" but let's not hardcode it).
+ * Here we use an existing resource "notification_sound_default" which is always expected to be
+ * found.
+ *
+ * @param resources Application's resources
+ * @return the built name prefix, or null if failed to build.
+ */
+ @Nullable
+ private static String buildNamePrefix(Resources resources) {
+ try {
+ return String.format("%s:%s/%s",
+ resources.getResourcePackageName(R.string.notification_sound_default),
+ resources.getResourceTypeName(R.string.notification_sound_default),
+ SOUND_NAME_RES_PREFIX);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Failed to build the prefix for the name of the resource.", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Process resource name to generate a valid resource name.
+ *
+ * @return a non-null String
+ */
+ private String sanitize(String input) {
+ if (input == null) {
+ return "";
+ }
+ return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase(Locale.ROOT);
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ final String defaultName = mCursor.getString(columnIndex);
+ if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
+ return defaultName;
+ }
+ TypedValue value = new TypedValue();
+ try {
+ // the name currently in the database is used to derive a name to match
+ // against resource names in this package
+ mResources.getValue(mNamePrefix + sanitize(defaultName), value,
+ /* resolveRefs= */ false);
+ } catch (Resources.NotFoundException e) {
+ Log.d(TAG, "Failed to get localized string. Using default string instead.", e);
+ return defaultName;
+ }
+ if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
+ Log.d(TAG, String.format("Replacing name %s with %s",
+ defaultName, value.string.toString()));
+ return value.string.toString();
+ } else {
+ Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
+ return defaultName;
+ }
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneAdapter.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneAdapter.java
new file mode 100644
index 000000000000..1f33aa2ce4d9
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneAdapter.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import static com.android.internal.widget.RecyclerView.NO_ID;
+
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.RingtoneManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckedTextView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.StringRes;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The adapter presents a list of ringtones which may include fixed item in the list and an action
+ * button at the end.
+ *
+ * The adapter handles three different types of items:
+ * <ul>
+ * <li>FIXED: Fixed items are items added to the top of the list. These items can not be modified
+ * and their position will never change.
+ * <li>DYNAMIC: Dynamic items are items from the ringtone manager. These items can be modified
+ * and their position can change.
+ * <li>FOOTER: A footer item is an added button to the end of the list. This item can be clicked
+ * but not selected and its position will never change.
+ * </ul>
+ */
+final class RingtoneAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ private static final int VIEW_TYPE_FIXED_ITEM = 0;
+ private static final int VIEW_TYPE_DYNAMIC_ITEM = 1;
+ private static final int VIEW_TYPE_ADD_RINGTONE_ITEM = 2;
+ private final Cursor mCursor;
+ private final List<Integer> mFixedItemTitles;
+ private final WorkRingtoneProvider mWorkRingtoneProvider;
+ private final RingtoneSelectionListener mRingtoneSelectionListener;
+ private final int mRowIDColumn;
+ private int mSelectedItem = -1;
+ @StringRes private Integer mAddRingtoneItemTitle;
+
+ /** Listener for ringtone selections. */
+ interface RingtoneSelectionListener {
+ void onRingtoneSelected(int position);
+ void onAddRingtoneSelected();
+ }
+ /** Provides a mean to detect work ringtones. */
+ interface WorkRingtoneProvider {
+ boolean isWorkRingtone(int position);
+
+ Drawable getWorkIconDrawable();
+ }
+
+ RingtoneAdapter(Cursor cursor, RingtoneSelectionListener ringtoneSelectionListener,
+ WorkRingtoneProvider workRingtoneProvider) {
+ mCursor = cursor;
+ mRingtoneSelectionListener = ringtoneSelectionListener;
+ mWorkRingtoneProvider = workRingtoneProvider;
+ mFixedItemTitles = new ArrayList<>();
+ mRowIDColumn = mCursor != null ? mCursor.getColumnIndex("_id") : -1;
+ setHasStableIds(true);
+ }
+
+ void setSelectedItem(int position) {
+ notifyItemChanged(mSelectedItem);
+ mSelectedItem = position;
+ notifyItemChanged(mSelectedItem);
+ }
+
+ void addTitleForFixedItem(@StringRes int textResId) {
+ mFixedItemTitles.add(textResId);
+ notifyItemInserted(mFixedItemTitles.size() - 1);
+ }
+
+ void addTitleForAddRingtoneItem(@StringRes int textResId) {
+ mAddRingtoneItemTitle = textResId;
+ notifyItemInserted(getItemCount() - 1);
+ }
+
+ @NotNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+
+ if (viewType == VIEW_TYPE_FIXED_ITEM) {
+ View fixedItemView = inflater.inflate(
+ com.android.internal.R.layout.select_dialog_singlechoice_material, parent,
+ false);
+
+ return new FixedItemViewHolder(fixedItemView, mRingtoneSelectionListener);
+ }
+
+ if (viewType == VIEW_TYPE_ADD_RINGTONE_ITEM) {
+ View addRingtoneItemView = inflater.inflate(R.layout.add_new_sound_item, parent, false);
+
+ return new AddRingtoneItemViewHolder(addRingtoneItemView, mRingtoneSelectionListener);
+ }
+
+ View view = inflater.inflate(R.layout.radio_with_work_badge, parent, false);
+
+ return new DynamicItemViewHolder(view, mRingtoneSelectionListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NotNull RecyclerView.ViewHolder holder, int position) {
+ if (holder instanceof FixedItemViewHolder) {
+ FixedItemViewHolder viewHolder = (FixedItemViewHolder) holder;
+
+ viewHolder.onBind(mFixedItemTitles.get(position),
+ /* isChecked= */ position == mSelectedItem);
+ return;
+ }
+ if (holder instanceof AddRingtoneItemViewHolder) {
+ AddRingtoneItemViewHolder viewHolder = (AddRingtoneItemViewHolder) holder;
+
+ viewHolder.onBind(mAddRingtoneItemTitle);
+ return;
+ }
+
+ if (!(holder instanceof DynamicItemViewHolder)) {
+ throw new IllegalArgumentException("holder type is not supported");
+ }
+
+ DynamicItemViewHolder viewHolder = (DynamicItemViewHolder) holder;
+ int pos = position - mFixedItemTitles.size();
+ if (!mCursor.moveToPosition(pos)) {
+ throw new IllegalStateException("Could not move cursor to position: " + pos);
+ }
+
+ Drawable workIcon = (mWorkRingtoneProvider != null)
+ && mWorkRingtoneProvider.isWorkRingtone(position)
+ ? mWorkRingtoneProvider.getWorkIconDrawable() : null;
+
+ viewHolder.onBind(mCursor.getString(RingtoneManager.TITLE_COLUMN_INDEX),
+ /* isChecked= */ position == mSelectedItem, workIcon);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (!mFixedItemTitles.isEmpty() && position < mFixedItemTitles.size()) {
+ return VIEW_TYPE_FIXED_ITEM;
+ }
+ if (mAddRingtoneItemTitle != null && position == getItemCount() - 1) {
+ return VIEW_TYPE_ADD_RINGTONE_ITEM;
+ }
+
+ return VIEW_TYPE_DYNAMIC_ITEM;
+ }
+
+ @Override
+ public int getItemCount() {
+ int itemCount = mFixedItemTitles.size() + mCursor.getCount();
+
+ if (mAddRingtoneItemTitle != null) {
+ itemCount++;
+ }
+
+ return itemCount;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ int itemViewType = getItemViewType(position);
+ if (itemViewType == VIEW_TYPE_FIXED_ITEM) {
+ // Since the item is a fixed item, then we can use the position as a stable ID
+ // since the order of the fixed items should never change.
+ return position;
+ }
+ if (itemViewType == VIEW_TYPE_DYNAMIC_ITEM && mCursor != null
+ && mCursor.moveToPosition(position - mFixedItemTitles.size())
+ && mRowIDColumn != -1) {
+ return mCursor.getLong(mRowIDColumn) + mFixedItemTitles.size();
+ }
+
+ // The position is either invalid or the item is the add ringtone item view, so no stable
+ // ID is returned. Add ringtone item view cannot be selected and only include an action
+ // buttons.
+ return NO_ID;
+ }
+
+ private static class DynamicItemViewHolder extends RecyclerView.ViewHolder {
+ private final CheckedTextView mTitleTextView;
+ private final ImageView mWorkIcon;
+
+ DynamicItemViewHolder(View itemView, RingtoneSelectionListener listener) {
+ super(itemView);
+
+ mTitleTextView = itemView.findViewById(R.id.checked_text_view);
+ mWorkIcon = itemView.findViewById(R.id.work_icon);
+ itemView.setOnClickListener(v -> listener.onRingtoneSelected(this.getLayoutPosition()));
+ }
+
+ void onBind(String title, boolean isChecked, Drawable workIcon) {
+ Objects.requireNonNull(mTitleTextView);
+ Objects.requireNonNull(mWorkIcon);
+
+ mTitleTextView.setText(title);
+ mTitleTextView.setChecked(isChecked);
+
+ if (workIcon == null) {
+ mWorkIcon.setVisibility(View.GONE);
+ } else {
+ mWorkIcon.setImageDrawable(workIcon);
+ mWorkIcon.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ private static class FixedItemViewHolder extends RecyclerView.ViewHolder {
+ private final CheckedTextView mTitleTextView;
+
+ FixedItemViewHolder(View itemView, RingtoneSelectionListener listener) {
+ super(itemView);
+
+ mTitleTextView = (CheckedTextView) itemView;
+ itemView.setOnClickListener(v -> listener.onRingtoneSelected(this.getLayoutPosition()));
+ }
+
+ void onBind(@StringRes int title, boolean isChecked) {
+ Objects.requireNonNull(mTitleTextView);
+
+ mTitleTextView.setText(title);
+ mTitleTextView.setChecked(isChecked);
+ }
+ }
+
+ private static class AddRingtoneItemViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mTitleTextView;
+
+ AddRingtoneItemViewHolder(View itemView, RingtoneSelectionListener listener) {
+ super(itemView);
+
+ mTitleTextView = itemView.findViewById(R.id.add_new_sound_text);
+ itemView.setOnClickListener(v -> listener.onAddRingtoneSelected());
+ }
+
+ void onBind(@StringRes int title) {
+ Objects.requireNonNull(mTitleTextView);
+
+ mTitleTextView.setText(title);
+ }
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index f591aa54a50e..4d7cf1cba161 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -16,46 +16,20 @@
package com.android.soundpicker;
-import android.content.ContentProvider;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.database.Cursor;
-import android.database.CursorWrapper;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.MediaStore;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.CursorAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
-import com.google.common.util.concurrent.FutureCallback;
-
import dagger.hilt.android.AndroidEntryPoint;
-import java.util.regex.Pattern;
-
/**
* The {@link RingtonePickerActivity} allows the user to choose one from all of the
* available ringtones. The chosen ringtone's URI will be persisted as a string.
@@ -63,106 +37,17 @@ import java.util.regex.Pattern;
* @see RingtoneManager#ACTION_RINGTONE_PICKER
*/
@AndroidEntryPoint(AppCompatActivity.class)
-public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity implements
- AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
- DialogInterface.OnDismissListener {
-
- private static final int POS_UNKNOWN = -1;
+public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity {
private static final String TAG = "RingtonePickerActivity";
-
- private static final int DELAY_MS_SELECTION_PLAYED = 300;
-
- private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
-
- private static final String SAVE_CLICKED_POS = "clicked_pos";
-
- private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
-
- private static final int ADD_FILE_REQUEST_CODE = 300;
+ // TODO: Use the extra key from RingtoneManager once it's added.
+ private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY";
+ private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false;
private RingtonePickerViewModel mRingtonePickerViewModel;
- private int mType;
-
- private Cursor mCursor;
- private Handler mHandler;
- private BadgedRingtoneAdapter mAdapter;
-
- /** Whether this list has the 'Silent' item. */
- private boolean mHasSilentItem;
-
- /** The Uri to place a checkmark next to. */
- private Uri mExistingUri;
-
- /** Whether this list has the 'Default' item. */
- private boolean mHasDefaultItem;
-
- /** The Uri to play when the 'Default' item is clicked. */
- private Uri mUriForDefaultItem;
-
- /** Id of the user to which the ringtone picker should list the ringtones */
- private int mPickerUserId;
-
- /**
- * Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
- */
- private long mCheckedItemId = -1;
-
private int mAttributesFlags;
- private boolean mShowOkCancelButtons;
-
- private AlertDialog mAlertDialog;
-
- private int mCheckedItem = POS_UNKNOWN;
-
- private final DialogInterface.OnClickListener mRingtoneClickListener =
- new DialogInterface.OnClickListener() {
-
- /*
- * On item clicked
- */
- public void onClick(DialogInterface dialog, int which) {
- if (which == mCursor.getCount() + mRingtonePickerViewModel.getFixedItemCount()) {
- // The "Add new ringtone" item was clicked. Start a file picker intent to select
- // only audio files (MIME type "audio/*")
- final Intent chooseFile = getMediaFilePickerIntent();
- startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
- return;
- }
-
- // Save the position of most recently clicked item
- setCheckedItem(which);
-
- // In the buttonless (watch-only) version, preemptively set our result since we won't
- // have another chance to do so before the activity closes.
- if (!mShowOkCancelButtons) {
- setSuccessResultWithRingtone(
- mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
- mUriForDefaultItem));
- }
-
- // Play clip
- playRingtone(which, 0);
- }
-
- };
- private final FutureCallback<Uri> mAddCustomRingtoneCallback = new FutureCallback<>() {
- @Override
- public void onSuccess(Uri ringtoneUri) {
- requeryForAdapter();
- }
-
- @Override
- public void onFailure(Throwable throwable) {
- Log.e(TAG, "Failed to add custom ringtone.", throwable);
- // Ringtone was not added, display error Toast
- Toast.makeText(RingtonePickerActivity.this.getApplicationContext(),
- R.string.unable_to_add_ringtone, Toast.LENGTH_SHORT).show();
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -170,300 +55,75 @@ public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity im
mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class);
- mHandler = new Handler();
-
Intent intent = getIntent();
- mPickerUserId = UserHandle.myUserId();
+ /**
+ * Id of the user to which the ringtone picker should list the ringtones
+ */
+ int pickerUserId = UserHandle.myUserId();
// Get the types of ringtones to show
- mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
+ int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
- mRingtonePickerViewModel.initRingtoneManager(mType);
- setupCursor();
- /*
- * Get whether to show the 'Default' item, and the URI to play when the
- * default is clicked
- */
- mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
- mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
- if (mUriForDefaultItem == null) {
- mUriForDefaultItem = RingtonePickerViewModel.getDefaultItemUriByType(mType);
+ // Get whether to show the 'Default' item, and the URI to play when the default is clicked
+ boolean hasDefaultItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ // The Uri to play when the 'Default' item is clicked.
+ Uri uriForDefaultItem =
+ intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+ if (uriForDefaultItem == null) {
+ uriForDefaultItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType);
}
- // Get whether to show the 'Silent' item
- mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ // Get whether this list has the 'Silent' item.
+ boolean hasSilentItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
// AudioAttributes flags
mAttributesFlags |= intent.getIntExtra(
RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
0 /*defaultValue == no flags*/);
- mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
-
- // The volume keys will control the stream that we are choosing a ringtone for
- setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
+ boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
// Get the URI whose list item should have a checkmark
- mExistingUri = intent
+ Uri existingUri = intent
.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
- // Create the list of ringtones and hold on to it so we can update later.
- mAdapter = new BadgedRingtoneAdapter(this, mCursor,
- /* isManagedProfile = */ UserManager.get(this).isManagedProfile(mPickerUserId));
-
- AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this,
- android.R.style.ThemeOverlay_Material_Dialog);
- alertDialogBuilder
- .setSingleChoiceItems(mAdapter, getCheckedItem(), mRingtoneClickListener)
- .setOnItemSelectedListener(this)
- .setOnDismissListener(this);
- if (mShowOkCancelButtons) {
- alertDialogBuilder
- .setPositiveButton(getString(com.android.internal.R.string.ok), this)
- .setNegativeButton(getString(com.android.internal.R.string.cancel), this);
+ String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+ if (title == null) {
+ title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType));
}
+ String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY);
+ RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType(
+ ringtonePickerCategory);
- String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
- alertDialogBuilder.setTitle(
- title != null ? title : getString(RingtonePickerViewModel.getTitleByType(mType)));
+ mRingtonePickerViewModel.init(new RingtonePickerViewModel.PickerConfig(title, pickerUserId,
+ ringtoneType, hasDefaultItem, uriForDefaultItem, hasSilentItem,
+ mAttributesFlags, existingUri, showOkCancelButtons, pickerType));
- mAlertDialog = alertDialogBuilder.show();
- ListView listView = mAlertDialog.getListView();
- if (listView != null) {
- // List view needs to gain focus in order for RSB to work.
- if (!listView.requestFocus()) {
- Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
- }
- prepareListView(listView);
- }
- if (savedInstanceState != null) {
- setCheckedItem(savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN));
- }
- }
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(SAVE_CLICKED_POS, getCheckedItem());
- }
+ // The volume keys will control the stream that we are choosing a ringtone for
+ setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
+ if (savedInstanceState == null) {
+ TabbedDialogFragment dialogFragment = new TabbedDialogFragment();
- if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
- mRingtonePickerViewModel.addRingtoneAsync(data.getData(),
- mType,
- mAddCustomRingtoneCallback,
- // Causes the callback to be executed on the main thread.
- ContextCompat.getMainExecutor(this.getApplicationContext()));
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG);
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+ dialogFragment.show(ft, TabbedDialogFragment.TAG);
}
}
- @Override
- public void onDismiss(DialogInterface dialog) {
- if (!isChangingConfigurations()) {
- finish();
- }
- }
@Override
public void onDestroy() {
mRingtonePickerViewModel.cancelPendingAsyncTasks();
- if (mAlertDialog != null && mAlertDialog.isShowing()) {
- mAlertDialog.dismiss();
- }
- if (mHandler != null) {
- mHandler.removeCallbacksAndMessages(null);
- }
- if (mCursor != null) {
- mCursor.close();
- mCursor = null;
- }
super.onDestroy();
}
- private void prepareListView(@NonNull ListView listView) {
- // Reset the static item count, as this method can be called multiple times
- mRingtonePickerViewModel.resetFixedItemCount();
-
- if (mHasDefaultItem) {
- int defaultItemPos = addDefaultRingtoneItem(listView);
-
- if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
- setCheckedItem(defaultItemPos);
- }
- }
-
- if (mHasSilentItem) {
- int silentItemPos = addSilentItem(listView);
-
- // The 'Silent' item should use a null Uri
- if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
- setCheckedItem(silentItemPos);
- }
- }
-
- if (getCheckedItem() == POS_UNKNOWN) {
- setCheckedItem(
- getListPosition(mRingtonePickerViewModel.getRingtonePosition(mExistingUri)));
- }
-
- // In the buttonless (watch-only) version, preemptively set our result since we won't
- // have another chance to do so before the activity closes.
- if (!mShowOkCancelButtons) {
- setSuccessResultWithRingtone(
- mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
- mUriForDefaultItem));
- }
- // If external storage is available, add a button to install sounds from storage.
- if (resolvesMediaFilePicker()
- && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- addNewSoundItem(listView);
- }
-
- // Enable context menu in ringtone items
- registerForContextMenu(listView);
- }
-
- /**
- * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
- * selected item position to match the new position of the chosen sound.
- *
- * This should only need to happen after adding or removing a ringtone.
- */
- private void requeryForAdapter() {
- // Refresh and set a new cursor, closing the old one.
- mRingtonePickerViewModel.initRingtoneManager(mType);
- setupCursor();
- mAdapter.changeCursor(mCursor);
-
- // Update checked item location.
- int checkedPosition = POS_UNKNOWN;
- for (int i = 0; i < mAdapter.getCount(); i++) {
- if (mAdapter.getItemId(i) == mCheckedItemId) {
- checkedPosition = getListPosition(i);
- break;
- }
- }
- if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
- checkedPosition = mRingtonePickerViewModel.getSilentItemPosition();
- }
- setCheckedItem(checkedPosition);
- }
-
- /**
- * Adds a static item to the top of the list. A static item is one that is not from the
- * RingtoneManager.
- *
- * @param listView The ListView to add to.
- * @param textResId The resource ID of the text for the item.
- * @return The position of the inserted item.
- */
- private int addStaticItem(@NonNull ListView listView, int textResId) {
- TextView textView = (TextView) getLayoutInflater().inflate(
- com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
- textView.setText(textResId);
- listView.addHeaderView(textView);
- mRingtonePickerViewModel.incrementFixedItemCount();
- return listView.getHeaderViewsCount() - 1;
- }
-
- private int addDefaultRingtoneItem(@NonNull ListView listView) {
- int defaultRingtoneItemPos = addStaticItem(listView,
- RingtonePickerViewModel.getDefaultRingtoneItemTextByType(mType));
- mRingtonePickerViewModel.setDefaultItemPosition(defaultRingtoneItemPos);
- return defaultRingtoneItemPos;
- }
-
- private int addSilentItem(@NonNull ListView listView) {
- int silentItemPos = addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
- mRingtonePickerViewModel.setSilentItemPosition(silentItemPos);
- return silentItemPos;
- }
-
- private void addNewSoundItem(@NonNull ListView listView) {
- View view = getLayoutInflater().inflate(R.layout.add_new_sound_item, listView,
- false /* attachToRoot */);
- TextView text = (TextView)view.findViewById(R.id.add_new_sound_text);
-
- text.setText(RingtonePickerViewModel.getAddNewItemTextByType(mType));
-
- listView.addFooterView(view);
- }
-
- private void setupCursor() {
- mCursor = new LocalizedCursor(
- mRingtonePickerViewModel.getRingtoneCursor(), getResources(), COLUMN_LABEL);
- }
-
- private int getCheckedItem() {
- return mCheckedItem;
- }
-
- private void setCheckedItem(int pos) {
- mCheckedItem = pos;
- ListView listView = mAlertDialog.getListView();
- if (listView != null) {
- listView.setItemChecked(pos, true);
- listView.smoothScrollToPosition(pos);
- }
- mCheckedItemId = mAdapter.getItemId(
- mRingtonePickerViewModel.itemPositionToRingtonePosition(pos));
- }
-
- /*
- * On click of Ok/Cancel buttons
- */
- public void onClick(DialogInterface dialog, int which) {
- boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
-
- if (positiveResult) {
- setSuccessResultWithRingtone(
- mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
- mUriForDefaultItem));
- } else {
- setResult(RESULT_CANCELED);
- }
-
- finish();
- }
-
- /*
- * On item selected via keys
- */
- public void onItemSelected(AdapterView parent, View view, int position, long id) {
- // footer view
- if (position >= mCursor.getCount() + mRingtonePickerViewModel.getFixedItemCount()) {
- return;
- }
-
- playRingtone(position, DELAY_MS_SELECTION_PLAYED);
-
- // In the buttonless (watch-only) version, preemptively set our result since we won't
- // have another chance to do so before the activity closes.
- if (!mShowOkCancelButtons) {
- setSuccessResultWithRingtone(
- mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri(getCheckedItem(),
- mUriForDefaultItem));
- }
- }
-
- public void onNothingSelected(AdapterView parent) {
- }
-
- private void playRingtone(int position, int delayMs) {
- mHandler.removeCallbacks(this);
- mRingtonePickerViewModel.setSampleItemPosition(position);
- mHandler.postDelayed(this, delayMs);
- }
-
- public void run() {
- mRingtonePickerViewModel.playRingtone(
- mRingtonePickerViewModel.itemPositionToRingtonePosition(
- mRingtonePickerViewModel.getSampleItemPosition()), mUriForDefaultItem,
- mAttributesFlags);
- }
-
@Override
protected void onStop() {
super.onStop();
@@ -476,155 +136,29 @@ public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity im
mRingtonePickerViewModel.onPause(isChangingConfigurations());
}
- private void setSuccessResultWithRingtone(Uri ringtoneUri) {
- setResult(RESULT_OK,
- new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
- }
-
- private int getListPosition(int ringtoneManagerPos) {
-
- // If the manager position is -1 (for not found), return that
- if (ringtoneManagerPos < 0) return ringtoneManagerPos;
-
- return ringtoneManagerPos + mRingtonePickerViewModel.getFixedItemCount();
- }
-
- private Intent getMediaFilePickerIntent() {
- final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
- chooseFile.setType("audio/*");
- chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
- new String[] { "audio/*", "application/ogg" });
- return chooseFile;
- }
-
- private boolean resolvesMediaFilePicker() {
- return getMediaFilePickerIntent().resolveActivity(getPackageManager()) != null;
- }
-
- private static class LocalizedCursor extends CursorWrapper {
-
- final int mTitleIndex;
- final Resources mResources;
- String mNamePrefix;
- final Pattern mSanitizePattern;
-
- LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
- super(cursor);
- mTitleIndex = mCursor.getColumnIndex(columnLabel);
- mResources = resources;
- mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
- if (mTitleIndex == -1) {
- Log.e(TAG, "No index for column " + columnLabel);
- mNamePrefix = null;
- } else {
- try {
- // Build the prefix for the name of the resource to look up
- // format is: "ResourcePackageName::ResourceTypeName/"
- // (the type name is expected to be "string" but let's not hardcode it).
- // Here we use an existing resource "notification_sound_default" which is
- // always expected to be found.
- mNamePrefix = String.format("%s:%s/%s",
- mResources.getResourcePackageName(R.string.notification_sound_default),
- mResources.getResourceTypeName(R.string.notification_sound_default),
- SOUND_NAME_RES_PREFIX);
- } catch (NotFoundException e) {
- mNamePrefix = null;
- }
- }
- }
-
- /**
- * Process resource name to generate a valid resource name.
- * @param input
- * @return a non-null String
- */
- private String sanitize(String input) {
- if (input == null) {
- return "";
- }
- return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase();
- }
-
- @Override
- public String getString(int columnIndex) {
- final String defaultName = mCursor.getString(columnIndex);
- if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
- return defaultName;
- }
- TypedValue value = new TypedValue();
- try {
- // the name currently in the database is used to derive a name to match
- // against resource names in this package
- mResources.getValue(mNamePrefix + sanitize(defaultName), value, false);
- } catch (NotFoundException e) {
- // no localized string, use the default string
- return defaultName;
- }
- if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
- Log.d(TAG, String.format("Replacing name %s with %s",
- defaultName, value.string.toString()));
- return value.string.toString();
- } else {
- Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
- return defaultName;
- }
- }
- }
-
- private class BadgedRingtoneAdapter extends CursorAdapter {
- private final boolean mIsManagedProfile;
-
- public BadgedRingtoneAdapter(Context context, Cursor cursor, boolean isManagedProfile) {
- super(context, cursor);
- mIsManagedProfile = isManagedProfile;
- }
-
- @Override
- public long getItemId(int position) {
- if (position < 0) {
- return position;
- }
- return super.getItemId(position);
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(context);
- return inflater.inflate(R.layout.radio_with_work_badge, parent, false);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- // Set text as the title of the ringtone
- ((TextView) view.findViewById(R.id.checked_text_view))
- .setText(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX));
-
- boolean isWorkRingtone = false;
- if (mIsManagedProfile) {
- /*
- * Display the work icon if the ringtone belongs to a work profile. We can tell that
- * a ringtone belongs to a work profile if the picker user is a managed profile, the
- * ringtone Uri is in external storage, and either the uri has no user id or has the
- * id of the picker user
- */
- Uri currentUri = mRingtonePickerViewModel.getRingtoneUri(cursor.getPosition());
- int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
- Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
-
- if (uriUserId == mPickerUserId && uriWithoutUserId.toString()
- .startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
- isWorkRingtone = true;
- }
- }
-
- ImageView workIcon = (ImageView) view.findViewById(R.id.work_icon);
- if(isWorkRingtone) {
- workIcon.setImageDrawable(getPackageManager().getUserBadgeForDensityNoBackground(
- UserHandle.of(mPickerUserId), -1 /* density */));
- workIcon.setVisibility(View.VISIBLE);
- } else {
- workIcon.setVisibility(View.GONE);
- }
+ /**
+ * Maps the ringtone picker category to the appropriate PickerType.
+ * If the category is null or the feature is still not released, then it defaults to sound
+ * picker.
+ *
+ * @param category the ringtone picker category.
+ * @return the corresponding picker type.
+ */
+ private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) {
+ if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) {
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ }
+
+ switch (category) {
+ case "android.intent.category.RINGTONE_PICKER_RINGTONE":
+ return RingtonePickerViewModel.PickerType.RINGTONE_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_SOUND":
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_VIBRATION":
+ return RingtonePickerViewModel.PickerType.VIBRATION_PICKER;
+ default:
+ Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker.");
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
}
}
}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java
index f045dc2f864c..914f16ab41df 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java
@@ -27,6 +27,7 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.Settings;
+import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,6 +51,7 @@ import javax.inject.Inject;
public final class RingtonePickerViewModel extends ViewModel {
static final int RINGTONE_TYPE_UNKNOWN = -1;
+
/**
* Keep the currently playing ringtone around when changing orientation, so that it
* can be stopped later, after the activity is recreated.
@@ -72,16 +74,87 @@ public final class RingtonePickerViewModel extends ViewModel {
private int mSampleItemPosition = ITEM_POSITION_UNKNOWN;
/** The position in the list of the 'Default' item. */
private int mDefaultItemPosition = ITEM_POSITION_UNKNOWN;
- /** The number of static items in the list. */
+ /** The number of fixed items in the list. */
private int mFixedItemCount;
private ListenableFuture<Uri> mAddCustomRingtoneFuture;
private RingtoneManager mRingtoneManager;
/**
+ * Stable ID for the ringtone that is currently selected (may be -1 if no ringtone is selected).
+ */
+ private long mSelectedItemId = -1;
+ private int mSelectedItemPosition = ITEM_POSITION_UNKNOWN;
+
+ /**
* The ringtone that's currently playing.
*/
private Ringtone mCurrentRingtone;
+ private PickerConfig mPickerConfig;
+
+ public enum PickerType {
+ RINGTONE_PICKER,
+ SOUND_PICKER,
+ VIBRATION_PICKER
+ }
+
+ /**
+ * Holds immutable info on the picker that should be displayed.
+ */
+ static final class PickerConfig {
+ public final String title;
+ /**
+ * Id of the user to which the ringtone picker should list the ringtones.
+ */
+ public final int userId;
+ /**
+ * Ringtone type.
+ */
+ public final int ringtoneType;
+ /**
+ * Whether this list has the 'Default' item.
+ */
+ public final boolean hasDefaultItem;
+ /**
+ * The Uri to play when the 'Default' item is clicked.
+ */
+ public final Uri uriForDefaultItem;
+ /**
+ * Whether this list has the 'Silent' item.
+ */
+ public final boolean hasSilentItem;
+ /**
+ * AudioAttributes flags.
+ */
+ public final int audioAttributesFlags;
+ /**
+ * The Uri to place a checkmark next to.
+ */
+ public final Uri existingUri;
+ /**
+ * In the buttonless (watch-only) version we don't show the OK/Cancel buttons.
+ */
+ public final boolean showOkCancelButtons;
+
+ public final PickerType mPickerType;
+
+ PickerConfig(String title, int userId, int ringtoneType,
+ boolean hasDefaultItem, Uri uriForDefaultItem, boolean hasSilentItem,
+ int audioAttributesFlags, Uri existingUri, boolean showOkCancelButtons,
+ PickerType pickerType) {
+ this.title = title;
+ this.userId = userId;
+ this.ringtoneType = ringtoneType;
+ this.hasDefaultItem = hasDefaultItem;
+ this.uriForDefaultItem = uriForDefaultItem;
+ this.hasSilentItem = hasSilentItem;
+ this.audioAttributesFlags = audioAttributesFlags;
+ this.existingUri = existingUri;
+ this.showOkCancelButtons = showOkCancelButtons;
+ this.mPickerType = pickerType;
+ }
+ }
+
@Inject
RingtonePickerViewModel(RingtoneManagerFactory ringtoneManagerFactory,
RingtoneFactory ringtoneFactory,
@@ -91,6 +164,13 @@ public final class RingtonePickerViewModel extends ViewModel {
mListeningExecutorService = listeningExecutorServiceFactory.createSingleThreadExecutor();
}
+ @NonNull
+ PickerConfig getPickerConfig() {
+ return requireNonNull(mPickerConfig,
+ "PickerConfig was never set. Did you forget to call "
+ + "RingtonePickerViewModel#init?");
+ }
+
@StringRes
static int getTitleByType(int ringtoneType) {
switch (ringtoneType) {
@@ -138,10 +218,11 @@ public final class RingtonePickerViewModel extends ViewModel {
}
}
- void initRingtoneManager(int type) {
+ void init(@NonNull PickerConfig pickerConfig) {
mRingtoneManager = mRingtoneManagerFactory.create();
- if (type != RINGTONE_TYPE_UNKNOWN) {
- mRingtoneManager.setType(type);
+ mPickerConfig = pickerConfig;
+ if (pickerConfig.ringtoneType != RINGTONE_TYPE_UNKNOWN) {
+ mRingtoneManager.setType(pickerConfig.ringtoneType);
}
}
@@ -166,7 +247,7 @@ public final class RingtonePickerViewModel extends ViewModel {
*/
void cancelPendingAsyncTasks() {
if (mAddCustomRingtoneFuture != null && !mAddCustomRingtoneFuture.isDone()) {
- mAddCustomRingtoneFuture.cancel(/*mayInterruptIfRunning=*/true);
+ mAddCustomRingtoneFuture.cancel(/* mayInterruptIfRunning= */ true);
}
}
@@ -180,36 +261,48 @@ public final class RingtonePickerViewModel extends ViewModel {
return mRingtoneManager.getCursor();
}
- Uri getRingtoneUri(int ringtonePosition) {
+ Uri getRingtoneUri(int position) {
requireNonNull(mRingtoneManager, RINGTONE_MANAGER_NULL_MESSAGE);
- return mRingtoneManager.getRingtoneUri(ringtonePosition);
+ return mRingtoneManager.getRingtoneUri(mapListPositionToRingtonePosition(position));
}
int getRingtonePosition(Uri uri) {
requireNonNull(mRingtoneManager, RINGTONE_MANAGER_NULL_MESSAGE);
- return mRingtoneManager.getRingtonePosition(uri);
+ return mapRingtonePositionToListPosition(mRingtoneManager.getRingtonePosition(uri));
}
/**
- * Returns the position of the item in the list before header views were added.
+ * Maps the item position in the list, to its equivalent position in the RingtoneManager.
*
- * @param itemPosition the position of item in the list with any added headers.
- * @return position of the item in the list ignoring headers.
+ * @param itemPosition the position of item in the list.
+ * @return position of the item in the RingtoneManager.
*/
- int itemPositionToRingtonePosition(int itemPosition) {
+ private int mapListPositionToRingtonePosition(int itemPosition) {
+ // If the manager position is -1 (for not found), then return that.
+ if (itemPosition < 0) return itemPosition;
+
return itemPosition - mFixedItemCount;
}
- int getFixedItemCount() {
- return mFixedItemCount;
+ /**
+ * Maps the item position in the RingtoneManager, to its equivalent position in the list.
+ *
+ * @param itemPosition the position of the item in the RingtoneManager.
+ * @return position of the item in the list.
+ */
+ private int mapRingtonePositionToListPosition(int itemPosition) {
+ // If the manager position is -1 (for not found), then return that.
+ if (itemPosition < 0) return itemPosition;
+
+ return itemPosition + mFixedItemCount;
}
void resetFixedItemCount() {
mFixedItemCount = 0;
}
- void incrementFixedItemCount() {
- mFixedItemCount++;
+ int incrementAndGetFixedItemCount() {
+ return mFixedItemCount++;
}
void setDefaultItemPosition(int defaultItemPosition) {
@@ -232,6 +325,22 @@ public final class RingtonePickerViewModel extends ViewModel {
mSampleItemPosition = sampleItemPosition;
}
+ public int getSelectedItemPosition() {
+ return mSelectedItemPosition;
+ }
+
+ public void setSelectedItemPosition(int selectedItemPosition) {
+ mSelectedItemPosition = selectedItemPosition;
+ }
+
+ public void setSelectedItemId(long selectedItemId) {
+ mSelectedItemId = selectedItemId;
+ }
+
+ public long getSelectedItemId() {
+ return mSelectedItemId;
+ }
+
void onPause(boolean isChangingConfigurations) {
if (!isChangingConfigurations) {
stopAnyPlayingRingtone();
@@ -247,19 +356,19 @@ public final class RingtonePickerViewModel extends ViewModel {
}
@Nullable
- Uri getCurrentlySelectedRingtoneUri(int checkedItem, Uri defaultUri) {
- if (checkedItem == ITEM_POSITION_UNKNOWN) {
- // When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
+ Uri getCurrentlySelectedRingtoneUri() {
+ if (mSelectedItemPosition == ITEM_POSITION_UNKNOWN) {
+ // When the selected item is POS_UNKNOWN, it is not the case we expected.
// We return null for this case.
return null;
- } else if (checkedItem == mDefaultItemPosition) {
+ } else if (mSelectedItemPosition == mDefaultItemPosition) {
// Use the default Uri that they originally gave us.
- return defaultUri;
- } else if (checkedItem == mSilentItemPosition) {
+ return mPickerConfig.uriForDefaultItem;
+ } else if (mSelectedItemPosition == mSilentItemPosition) {
// Use a null Uri for the 'Silent' item.
return null;
} else {
- return getRingtoneUri(itemPositionToRingtonePosition(checkedItem));
+ return getRingtoneUri(mSelectedItemPosition);
}
}
@@ -280,7 +389,8 @@ public final class RingtonePickerViewModel extends ViewModel {
mCurrentRingtone.setStreamType(mRingtoneManager.inferStreamType());
}
} else {
- mCurrentRingtone = mRingtoneManager.getRingtone(position);
+ mCurrentRingtone = mRingtoneManager.getRingtone(
+ mapListPositionToRingtonePosition(position));
}
if (mCurrentRingtone != null) {
diff --git a/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java b/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java
new file mode 100644
index 000000000000..e07d1095bed2
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.app.Activity;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.common.util.concurrent.FutureCallback;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A fragment that will display a picker used to select sound or silent. It also includes the
+ * ability to add custom sounds.
+ */
+@AndroidEntryPoint(Fragment.class)
+public class SoundPickerFragment extends Hilt_SoundPickerFragment {
+
+ private static final String TAG = "SoundPickerFragment";
+ private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
+ private static final int POS_UNKNOWN = -1;
+
+ private RingtonePickerViewModel.PickerConfig mPickerConfig;
+ private boolean mIsManagedProfile;
+ private RingtonePickerViewModel mRingtonePickerViewModel;
+ private RingtoneAdapter mRingtoneAdapter;
+ private RecyclerView mSoundRecyclerView;
+
+ private final RingtoneAdapter.WorkRingtoneProvider mWorkRingtoneProvider =
+ new RingtoneAdapter.WorkRingtoneProvider() {
+ private Drawable mWorkIconDrawable;
+ @Override
+ public boolean isWorkRingtone(int position) {
+ if (mIsManagedProfile) {
+ /*
+ * Display the w ork icon if the ringtone belongs to a work profile. We
+ * can tell that a ringtone belongs to a work profile if the picker user
+ * is a managed profile, the ringtone Uri is in external storage, and
+ * either the uri has no user id or has the id of the picker user
+ */
+ Uri currentUri = mRingtonePickerViewModel.getRingtoneUri(position);
+ int uriUserId = ContentProvider.getUserIdFromUri(currentUri,
+ mPickerConfig.userId);
+ Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
+
+ return uriUserId == mPickerConfig.userId
+ && uriWithoutUserId.toString().startsWith(
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString());
+ }
+
+ return false;
+ }
+
+ @Override
+ public Drawable getWorkIconDrawable() {
+ if (mWorkIconDrawable == null) {
+ mWorkIconDrawable = requireActivity().getPackageManager()
+ .getUserBadgeForDensityNoBackground(
+ UserHandle.of(mPickerConfig.userId), /* density= */ -1);
+ }
+
+ return mWorkIconDrawable;
+ }
+ };
+
+ private final FutureCallback<Uri> mAddCustomRingtoneCallback = new FutureCallback<>() {
+ @Override
+ public void onSuccess(Uri ringtoneUri) {
+ requeryForAdapter();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ Log.e(TAG, "Failed to add custom ringtone.", throwable);
+ // Ringtone was not added, display error Toast
+ Toast.makeText(requireActivity().getApplicationContext(),
+ R.string.unable_to_add_ringtone, Toast.LENGTH_SHORT).show();
+ }
+ };
+
+ ActivityResultLauncher<Intent> mActivityResultLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ new ActivityResultCallback<ActivityResult>() {
+ @Override
+ public void onActivityResult(ActivityResult result) {
+ if (result.getResultCode() == Activity.RESULT_OK) {
+ // There are no request codes
+ Intent data = result.getData();
+ mRingtonePickerViewModel.addRingtoneAsync(data.getData(),
+ mPickerConfig.ringtoneType,
+ mAddCustomRingtoneCallback,
+ // Causes the callback to be executed on the main thread.
+ ContextCompat.getMainExecutor(
+ requireActivity().getApplicationContext()));
+ }
+ }
+ });
+
+ private final RingtoneAdapter.RingtoneSelectionListener mRingtoneSelectionListener =
+ new RingtoneAdapter.RingtoneSelectionListener() {
+ @Override
+ public void onRingtoneSelected(int position) {
+ SoundPickerFragment.this.setSelectedItem(position);
+
+ // In the buttonless (watch-only) version, preemptively set our result since
+ // we won't have another chance to do so before the activity closes.
+ if (!mPickerConfig.showOkCancelButtons) {
+ setSuccessResultWithRingtone(
+ mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri());
+ }
+
+ // Play clip
+ playRingtone(position);
+ }
+
+ @Override
+ public void onAddRingtoneSelected() {
+ // The "Add new ringtone" item was clicked. Start a file picker intent to
+ // select only audio files (MIME type "audio/*")
+ final Intent chooseFile = getMediaFilePickerIntent();
+ mActivityResultLauncher.launch(chooseFile);
+ }
+ };
+
+ public SoundPickerFragment() {
+ super(R.layout.fragment_sound_picker);
+ }
+
+ @Override
+ public void onViewCreated(@NotNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get(
+ RingtonePickerViewModel.class);
+ mSoundRecyclerView = view.findViewById(R.id.recycler_view);
+ Objects.requireNonNull(mSoundRecyclerView);
+
+ mPickerConfig = mRingtonePickerViewModel.getPickerConfig();
+ mIsManagedProfile = UserManager.get(requireActivity()).isManagedProfile(
+ mPickerConfig.userId);
+
+ mRingtoneAdapter = createRingtoneAdapter();
+ mSoundRecyclerView.setHasFixedSize(true);
+ mSoundRecyclerView.setAdapter(mRingtoneAdapter);
+ mSoundRecyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));
+ setSelectedItem(mRingtonePickerViewModel.getSelectedItemPosition());
+ prepareRecyclerView(mSoundRecyclerView);
+ }
+
+ private void prepareRecyclerView(@NonNull RecyclerView recyclerView) {
+ // Reset the static item count, as this method can be called multiple times
+ mRingtonePickerViewModel.resetFixedItemCount();
+
+ if (mPickerConfig.hasDefaultItem) {
+ int defaultItemPos = addDefaultRingtoneItem();
+
+ if (getSelectedItem() == POS_UNKNOWN
+ && RingtoneManager.isDefault(mPickerConfig.existingUri)) {
+ setSelectedItem(defaultItemPos);
+ }
+ }
+
+ if (mPickerConfig.hasSilentItem) {
+ int silentItemPos = addSilentItem();
+
+ // The 'Silent' item should use a null Uri
+ if (getSelectedItem() == POS_UNKNOWN && mPickerConfig.existingUri == null) {
+ setSelectedItem(silentItemPos);
+ }
+ }
+
+ if (getSelectedItem() == POS_UNKNOWN) {
+ setSelectedItem(
+ mRingtonePickerViewModel.getRingtonePosition(mPickerConfig.existingUri));
+ }
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mPickerConfig.showOkCancelButtons) {
+ setSuccessResultWithRingtone(
+ mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri());
+ }
+ // If external storage is available, add a button to install sounds from storage.
+ if (resolvesMediaFilePicker()
+ && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ addNewSoundItem();
+ }
+
+ // Enable context menu in ringtone items
+ registerForContextMenu(recyclerView);
+ }
+
+ /**
+ * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
+ * selected item position to match the new position of the chosen sound.
+ * <p>
+ * This should only need to happen after adding or removing a ringtone.
+ */
+ private void requeryForAdapter() {
+ // Refresh and set a new cursor, closing the old one.
+ mRingtonePickerViewModel.init(mPickerConfig);
+ mRingtoneAdapter = createRingtoneAdapter();
+ mSoundRecyclerView.setAdapter(mRingtoneAdapter);
+ prepareRecyclerView(mSoundRecyclerView);
+
+ // Update selected item location.
+ int selectedPosition = POS_UNKNOWN;
+ for (int i = 0; i < mRingtoneAdapter.getItemCount(); i++) {
+ if (mRingtoneAdapter.getItemId(i) == mRingtonePickerViewModel.getSelectedItemId()) {
+ selectedPosition = i;
+ break;
+ }
+ }
+ if (mPickerConfig.hasSilentItem && selectedPosition == POS_UNKNOWN) {
+ selectedPosition = mRingtonePickerViewModel.getSilentItemPosition();
+ }
+ setSelectedItem(selectedPosition);
+ }
+
+ private void playRingtone(int position) {
+ mRingtonePickerViewModel.setSampleItemPosition(position);
+ mRingtonePickerViewModel.playRingtone(mRingtonePickerViewModel.getSampleItemPosition(),
+ mPickerConfig.uriForDefaultItem, mPickerConfig.audioAttributesFlags);
+ }
+
+ private boolean resolvesMediaFilePicker() {
+ return getMediaFilePickerIntent().resolveActivity(requireActivity().getPackageManager())
+ != null;
+ }
+
+ private Intent getMediaFilePickerIntent() {
+ final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.setType("audio/*");
+ chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+ new String[]{"audio/*", "application/ogg"});
+ return chooseFile;
+ }
+
+ private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+ requireActivity().setResult(Activity.RESULT_OK,
+ new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+ }
+
+ private int getSelectedItem() {
+ return mRingtonePickerViewModel.getSelectedItemPosition();
+ }
+
+ private void setSelectedItem(int pos) {
+ Objects.requireNonNull(mRingtoneAdapter);
+ mRingtonePickerViewModel.setSelectedItemPosition(pos);
+ mRingtoneAdapter.setSelectedItem(pos);
+ mRingtonePickerViewModel.setSelectedItemId(mRingtoneAdapter.getItemId(pos));
+ mSoundRecyclerView.scrollToPosition(pos);
+ }
+
+ /**
+ * Adds a fixed item to the fixed items list . A fixed item is one that is not from
+ * the RingtoneManager.
+ *
+ * @param textResId The resource ID of the text for the item.
+ * @return The position of the inserted item.
+ */
+ private int addFixedItem(int textResId) {
+ mRingtoneAdapter.addTitleForFixedItem(textResId);
+ return mRingtonePickerViewModel.incrementAndGetFixedItemCount();
+ }
+
+ private int addDefaultRingtoneItem() {
+ int defaultRingtoneItemPos = addFixedItem(
+ RingtonePickerViewModel.getDefaultRingtoneItemTextByType(
+ mPickerConfig.ringtoneType));
+ mRingtonePickerViewModel.setDefaultItemPosition(defaultRingtoneItemPos);
+ return defaultRingtoneItemPos;
+ }
+
+ private int addSilentItem() {
+ int silentItemPos = addFixedItem(com.android.internal.R.string.ringtone_silent);
+ mRingtonePickerViewModel.setSilentItemPosition(silentItemPos);
+ return silentItemPos;
+ }
+
+ private void addNewSoundItem() {
+ mRingtoneAdapter.addTitleForAddRingtoneItem(
+ RingtonePickerViewModel.getAddNewItemTextByType(mPickerConfig.ringtoneType));
+ }
+
+ private RingtoneAdapter createRingtoneAdapter() {
+ LocalizedCursor cursor = new LocalizedCursor(
+ mRingtonePickerViewModel.getRingtoneCursor(), getResources(), COLUMN_LABEL);
+ return new RingtoneAdapter(cursor, mRingtoneSelectionListener, mWorkRingtoneProvider);
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java b/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java
new file mode 100644
index 000000000000..63140d21516e
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import static android.app.Activity.RESULT_CANCELED;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A dialog fragment with a sound and/or vibration tab based on the picker type.
+ * <ul>
+ * <li> Ringtone Pickers will display both sound and vibration tabs.
+ * <li> Sound Pickers will only display the sound tab.
+ * <li> Vibration Pickers will only display the vibration tab.
+ * </ul>
+ */
+@AndroidEntryPoint(DialogFragment.class)
+public class TabbedDialogFragment extends Hilt_TabbedDialogFragment {
+
+ static final String TAG = "TabbedDialogFragment";
+
+ private RingtonePickerViewModel mRingtonePickerViewModel;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mRingtonePickerViewModel = new ViewModelProvider(requireActivity()).get(
+ RingtonePickerViewModel.class);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireActivity(),
+ android.R.style.ThemeOverlay_Material_Dialog)
+ .setTitle(mRingtonePickerViewModel.getPickerConfig().title);
+ // Do not show OK/Cancel buttons in the buttonless (watch-only) version.
+ if (mRingtonePickerViewModel.getPickerConfig().showOkCancelButtons) {
+ dialogBuilder
+ .setPositiveButton(getString(com.android.internal.R.string.ok),
+ (dialog, whichButton) -> {
+ setSuccessResultWithRingtone(
+ mRingtonePickerViewModel.getCurrentlySelectedRingtoneUri());
+ requireActivity().finish();
+ })
+ .setNegativeButton(getString(com.android.internal.R.string.cancel),
+ (dialog, whichButton) -> {
+ requireActivity().setResult(RESULT_CANCELED);
+ requireActivity().finish();
+ });
+ }
+
+ View view = buildTabbedView(requireActivity().getLayoutInflater());
+ dialogBuilder.setView(view);
+
+ return dialogBuilder.create();
+ }
+
+ @Override
+ public void onCancel(@NonNull @NotNull DialogInterface dialog) {
+ super.onCancel(dialog);
+ if (!requireActivity().isChangingConfigurations()) {
+ requireActivity().finish();
+ }
+ }
+
+ @Override
+ public void onDismiss(@NonNull @NotNull DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (!requireActivity().isChangingConfigurations()) {
+ requireActivity().finish();
+ }
+ }
+
+ private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+ requireActivity().setResult(Activity.RESULT_OK,
+ new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+ }
+
+ /**
+ * Inflates the tabbed layout view and adds the required fragments. If there's only one
+ * fragment to display, then the tab area is hidden.
+ * @param inflater The LayoutInflater that is used to inflate the tabbed view.
+ * @return The tabbed view.
+ */
+ private View buildTabbedView(@NonNull LayoutInflater inflater) {
+ View view = inflater.inflate(R.layout.fragment_tabbed_dialog, null, false);
+ TabLayout tabLayout = view.findViewById(R.id.tabLayout);
+ ViewPager2 viewPager = view.findViewById(R.id.masterViewPager);
+ Objects.requireNonNull(tabLayout);
+ Objects.requireNonNull(viewPager);
+
+ ViewPagerAdapter adapter = new ViewPagerAdapter(requireActivity());
+ addFragments(adapter);
+
+ if (adapter.getItemCount() == 1) {
+ // Hide the tab area since there's only one fragment to display.
+ tabLayout.setVisibility(View.GONE);
+ }
+
+ viewPager.setAdapter(adapter);
+ new TabLayoutMediator(tabLayout, viewPager,
+ (tab, position) -> tab.setText(adapter.getTitle(position))).attach();
+
+ return view;
+ }
+
+ /**
+ * Adds the appropriate fragments to the adapter based on the PickerType.
+ *
+ * @param adapter The adapter to add the fragments to.
+ */
+ private void addFragments(ViewPagerAdapter adapter) {
+ switch (mRingtonePickerViewModel.getPickerConfig().mPickerType) {
+ case RINGTONE_PICKER:
+ adapter.addFragment(getString(R.string.sound_page_title),
+ new SoundPickerFragment());
+ adapter.addFragment(getString(R.string.vibration_page_title),
+ new VibrationPickerFragment());
+ break;
+ case SOUND_PICKER:
+ adapter.addFragment(getString(R.string.sound_page_title),
+ new SoundPickerFragment());
+ break;
+ case VIBRATION_PICKER:
+ adapter.addFragment(getString(R.string.vibration_page_title),
+ new VibrationPickerFragment());
+ break;
+ default:
+ adapter.addFragment(getString(R.string.sound_page_title),
+ new SoundPickerFragment());
+ break;
+ }
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java b/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java
new file mode 100644
index 000000000000..356b9aee2c16
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import androidx.fragment.app.Fragment;
+
+/**
+ * A fragment that will display a picker used to select vibration.
+ */
+public class VibrationPickerFragment extends Fragment {
+
+ public VibrationPickerFragment() {
+ super(R.layout.fragment_vibration_picker);
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java b/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java
new file mode 100644
index 000000000000..179068e9f20f
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An adapter used to populate pages inside a ViewPager.
+ */
+public class ViewPagerAdapter extends FragmentStateAdapter {
+
+ private final List<Fragment> mFragments = new ArrayList<>();
+ private final List<String> mTitles = new ArrayList<>();
+
+ public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
+ super(fragmentActivity);
+ }
+
+ /**
+ * Adds a fragment and page title to the adapter.
+ * @param title the title of the page in the ViewPager.
+ * @param fragment the fragment that will be inflated on this page.
+ */
+ public void addFragment(String title, Fragment fragment) {
+ mTitles.add(title);
+ mFragments.add(fragment);
+ }
+
+ /**
+ * Returns the title of the requested page.
+ * @param position the position of the page in the Viewpager.
+ * @return The title of the requested page.
+ */
+ public String getTitle(int position) {
+ return mTitles.get(position);
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return Objects.requireNonNull(mFragments.get(position),
+ "Could not find a fragment using position: " + position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mFragments.size();
+ }
+}
diff --git a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java b/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
index 9ef3aa3b245f..659aae8188dd 100644
--- a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
+++ b/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
@@ -112,7 +112,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testInitRingtoneManager_whenTypeIsUnknown_createManagerButDoNotSetType() {
- mViewModel.initRingtoneManager(RINGTONE_TYPE_UNKNOWN);
+ mViewModel.init(createPickerConfig(RINGTONE_TYPE_UNKNOWN));
verify(mMockRingtoneManagerFactory).create();
verify(mMockRingtoneManager, never()).setType(anyInt());
@@ -120,7 +120,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testInitRingtoneManager_whenTypeIsNotUnknown_createManagerAndSetType() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_NOTIFICATION);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_NOTIFICATION));
verify(mMockRingtoneManagerFactory).create();
verify(mMockRingtoneManager).setType(RingtoneManager.TYPE_NOTIFICATION);
@@ -129,14 +129,14 @@ public class RingtonePickerViewModelTest {
@Test
public void testGetStreamType_returnsTheCorrectStreamType() {
when(mMockRingtoneManager.inferStreamType()).thenReturn(AudioManager.STREAM_ALARM);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
assertEquals(mViewModel.getRingtoneStreamType(), AudioManager.STREAM_ALARM);
}
@Test
public void testGetRingtoneCursor_returnsTheCorrectRingtoneCursor() {
when(mMockRingtoneManager.getCursor()).thenReturn(mMockCursor);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
assertEquals(mViewModel.getRingtoneCursor(), mMockCursor);
}
@@ -144,14 +144,14 @@ public class RingtonePickerViewModelTest {
public void testGetRingtoneUri_returnsTheCorrectRingtoneUri() {
Uri expectedUri = DEFAULT_URI;
when(mMockRingtoneManager.getRingtoneUri(anyInt())).thenReturn(expectedUri);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
Uri actualUri = mViewModel.getRingtoneUri(DEFAULT_RINGTONE_POSITION);
assertEquals(actualUri, expectedUri);
}
@Test
public void testOnPause_withChangingConfigurationTrue_doNotStopPlayingRingtone() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
verifyRingtonePlayCalledAndMockPlayingState(mMockRingtone);
@@ -161,7 +161,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testOnPause_withChangingConfigurationFalse_stopPlayingRingtone() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -172,7 +172,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testOnViewModelRecreated_previousRingtoneCanStillBeStopped() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
Ringtone mockRingtone1 = createMockRingtone();
Ringtone mockRingtone2 = createMockRingtone();
@@ -186,7 +186,7 @@ public class RingtonePickerViewModelTest {
verify(mockRingtone1, never()).stop();
mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory,
mMockListeningExecutorServiceFactory);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -197,7 +197,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testOnStop_withChangingConfigurationTrueAndDefaultRingtonePlaying_saveRingtone() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -208,7 +208,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testOnStop_withChangingConfigurationTrueAndCurrentRingtonePlaying_saveRingtone() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -226,7 +226,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testOnStop_withChangingConfigurationFalse_stopPlayingRingtone() {
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -237,21 +237,24 @@ public class RingtonePickerViewModelTest {
@Test
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsUnknown_returnsNull() {
- Uri uri = mViewModel.getCurrentlySelectedRingtoneUri(POS_UNKNOWN, DEFAULT_URI);
+ mViewModel.setSelectedItemPosition(POS_UNKNOWN);
+ Uri uri = mViewModel.getCurrentlySelectedRingtoneUri();
assertNull(uri);
}
@Test
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsDefaultPos_returnsDefaultUri() {
Uri expectedUri = DEFAULT_URI;
- Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri(DEFAULT_RINGTONE_POSITION,
- expectedUri);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
+ mViewModel.setSelectedItemPosition(DEFAULT_RINGTONE_POSITION);
+ Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri();
assertEquals(actualUri, expectedUri);
}
@Test
public void testGetCurrentlySelectedRingtoneUri_checkedItemIsSilentPos_returnsNull() {
- Uri uri = mViewModel.getCurrentlySelectedRingtoneUri(SILENT_RINGTONE_POSITION, DEFAULT_URI);
+ mViewModel.setSelectedItemPosition(SILENT_RINGTONE_POSITION);
+ Uri uri = mViewModel.getCurrentlySelectedRingtoneUri();
assertNull(uri);
}
@@ -266,7 +269,7 @@ public class RingtonePickerViewModelTest {
RingtoneManager.TYPE_NOTIFICATION)).thenReturn(DEFAULT_URI);
mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory,
mMockListeningExecutorServiceFactory);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.addRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, mockCallback,
mMainThreadExecutor);
verify(mockCallback, never()).onFailure(any());
@@ -290,7 +293,7 @@ public class RingtonePickerViewModelTest {
RingtoneManager.TYPE_NOTIFICATION)).thenReturn(DEFAULT_URI);
mViewModel = new RingtonePickerViewModel(mMockRingtoneManagerFactory, mMockRingtoneFactory,
mMockListeningExecutorServiceFactory);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.addRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, mockCallback1,
mMainThreadExecutor);
verify(mockCallback1, never()).onFailure(any());
@@ -312,7 +315,7 @@ public class RingtonePickerViewModelTest {
when(mMockRingtoneManager.addCustomExternalRingtone(DEFAULT_URI,
RingtoneManager.TYPE_NOTIFICATION)).thenReturn(expectedUri);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.addRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, mockCallback,
mMainThreadExecutor);
@@ -330,7 +333,7 @@ public class RingtonePickerViewModelTest {
when(mMockRingtoneManager.addCustomExternalRingtone(any(), anyInt())).thenThrow(
IOException.class);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.addRingtoneAsync(DEFAULT_URI, RingtoneManager.TYPE_NOTIFICATION, mockCallback,
mMainThreadExecutor);
@@ -342,8 +345,9 @@ public class RingtonePickerViewModelTest {
public void testGetCurrentlySelectedRingtoneUri_checkedItemRingtonePos_returnsTheCorrectUri() {
Uri expectedUri = DEFAULT_URI;
when(mMockRingtoneManager.getRingtoneUri(RINGTONE_POSITION)).thenReturn(expectedUri);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
- Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri(RINGTONE_POSITION, DEFAULT_URI);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
+ mViewModel.setSelectedItemPosition(RINGTONE_POSITION);
+ Uri actualUri = mViewModel.getCurrentlySelectedRingtoneUri();
verify(mMockRingtoneManager).getRingtoneUri(RINGTONE_POSITION);
assertEquals(actualUri, expectedUri);
@@ -353,7 +357,7 @@ public class RingtonePickerViewModelTest {
public void testPlayRingtone_stopsPreviouslyRunningRingtone() {
// Start playing the first ringtone
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
@@ -369,7 +373,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testPlayRingtone_samplePosEqualToSilentPos_onlyStopPlayingRingtone() {
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.playRingtone(DEFAULT_RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
verifyRingtonePlayCalledAndMockPlayingState(mMockDefaultRingtone);
@@ -388,7 +392,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testPlayRingtone_samplePosEqualToDefaultPos_playDefaultRingtone() {
mViewModel.setSampleItemPosition(DEFAULT_RINGTONE_POSITION);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
when(mMockRingtoneManager.inferStreamType()).thenReturn(AudioManager.STREAM_ALARM);
@@ -403,7 +407,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testPlayRingtone_samplePosNotEqualToDefaultPos_playRingtone() {
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
AudioAttributes.FLAG_AUDIBILITY_ENFORCED);
@@ -417,7 +421,7 @@ public class RingtonePickerViewModelTest {
@Test
public void testPlayRingtone_withNoAttributeFlags_doNotUpdateRingtoneAttributesFlags() {
mViewModel.setSampleItemPosition(RINGTONE_POSITION);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
mViewModel.playRingtone(RINGTONE_POSITION, DEFAULT_URI,
NO_ATTRIBUTES_FLAGS);
@@ -430,7 +434,7 @@ public class RingtonePickerViewModelTest {
public void testGetRingtonePosition_returnsTheCorrectRingtonePosition() {
int expectedPosition = 1;
when(mMockRingtoneManager.getRingtonePosition(any())).thenReturn(expectedPosition);
- mViewModel.initRingtoneManager(RingtoneManager.TYPE_RINGTONE);
+ mViewModel.init(createPickerConfig(RingtoneManager.TYPE_RINGTONE));
int actualPosition = mViewModel.getRingtonePosition(DEFAULT_URI);
assertEquals(actualPosition, expectedPosition);
@@ -553,4 +557,13 @@ public class RingtonePickerViewModelTest {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
}
+
+ private RingtonePickerViewModel.PickerConfig createPickerConfig(int ringtoneType) {
+ return new RingtonePickerViewModel.PickerConfig("Phone ringtone", /* userId= */ 1,
+ ringtoneType, /* hasDefaultItem= */ true,
+ /* uriForDefaultItem= */ DEFAULT_URI, /* hasSilentItem= */ true,
+ /* audioAttributesFlags= */0, /* existingUri= */ Uri.parse(""),
+ /* showOkCancelButtons= */ true,
+ RingtonePickerViewModel.PickerType.RINGTONE_PICKER);
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 75bf2813a321..13acde206247 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -121,8 +121,8 @@ fun FooterActions(
}
}
- val backgroundColor = colorAttr(R.attr.underSurfaceColor)
- val contentColor = LocalAndroidColorScheme.current.deprecated.textColorPrimary
+ val backgroundColor = colorAttr(R.attr.underSurface)
+ val contentColor = LocalAndroidColorScheme.current.onSurface
val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
val backgroundModifier =
remember(
@@ -268,7 +268,7 @@ private fun NumberButton(
val interactionSource = remember { MutableInteractionSource() }
Expandable(
- color = colorAttr(R.attr.offStateColor),
+ color = colorAttr(R.attr.shadeInactive),
shape = CircleShape,
onClick = onClick,
interactionSource = interactionSource,
@@ -287,7 +287,7 @@ private fun NumberButton(
number.toString(),
modifier = Modifier.align(Alignment.Center),
style = MaterialTheme.typography.bodyLarge,
- color = LocalAndroidColorScheme.current.deprecated.textColorPrimary,
+ color = colorAttr(R.attr.onShadeInactiveVariant),
// TODO(b/242040009): This should only use a standard text style instead and
// should not override the text size.
fontSize = 18.sp,
@@ -305,7 +305,7 @@ private fun NumberButton(
@Composable
private fun NewChangesDot(modifier: Modifier = Modifier) {
val contentDescription = stringResource(R.string.fgs_dot_content_description)
- val color = LocalAndroidColorScheme.current.deprecated.colorAccentTertiary
+ val color = LocalAndroidColorScheme.current.tertiary
Canvas(modifier.size(12.dp).semantics { this.contentDescription = contentDescription }) {
drawCircle(color)
@@ -323,10 +323,9 @@ private fun TextButton(
) {
Expandable(
shape = CircleShape,
- color = colorAttr(R.attr.underSurfaceColor),
- contentColor = LocalAndroidColorScheme.current.deprecated.textColorSecondary,
- borderStroke =
- BorderStroke(1.dp, LocalAndroidColorScheme.current.deprecated.colorBackground),
+ color = colorAttr(R.attr.underSurface),
+ contentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
+ borderStroke = BorderStroke(1.dp, colorAttr(R.attr.onShadeActive)),
modifier = modifier.padding(horizontal = 4.dp),
onClick = onClick,
) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 648ef03895cd..b9d66432b5f2 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,8 +33,8 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
@@ -51,7 +51,12 @@ class AnimatableClockView @JvmOverloads constructor(
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- var logBuffer: LogBuffer? = null
+ var messageBuffer: MessageBuffer? = null
+ set(value) {
+ logger = if (value != null) Logger(value, TAG) else null
+ }
+
+ private var logger: Logger? = null
private val time = Calendar.getInstance()
@@ -129,7 +134,7 @@ class AnimatableClockView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- logBuffer?.log(TAG, DEBUG, "onAttachedToWindow")
+ logger?.d("onAttachedToWindow")
refreshFormat()
}
@@ -145,39 +150,32 @@ class AnimatableClockView @JvmOverloads constructor(
time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
contentDescription = DateFormat.format(descFormat, time)
val formattedText = DateFormat.format(format, time)
- logBuffer?.log(TAG, DEBUG,
- { str1 = formattedText?.toString() },
- { "refreshTime: new formattedText=$str1" }
- )
+ logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
// Setting text actually triggers a layout pass (because the text view is set to
// wrap_content width and TextView always relayouts for this). Avoid needless
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
- logBuffer?.log(TAG, DEBUG,
- { str1 = formattedText?.toString() },
- { "refreshTime: done setting new time text to: $str1" }
- )
+ logger?.d({ "refreshTime: done setting new time text to: $str1" }) {
+ str1 = formattedText?.toString()
+ }
// Because the TextLayout may mutate under the hood as a result of the new text, we
// notify the TextAnimator that it may have changed and request a measure/layout. A
// crash will occur on the next invocation of setTextStyle if the layout is mutated
// without being notified TextInterpolator being notified.
if (layout != null) {
textAnimator?.updateLayout(layout)
- logBuffer?.log(TAG, DEBUG, "refreshTime: done updating textAnimator layout")
+ logger?.d("refreshTime: done updating textAnimator layout")
}
requestLayout()
- logBuffer?.log(TAG, DEBUG, "refreshTime: after requestLayout")
+ logger?.d("refreshTime: after requestLayout")
}
}
fun onTimeZoneChanged(timeZone: TimeZone?) {
time.timeZone = timeZone
refreshFormat()
- logBuffer?.log(TAG, DEBUG,
- { str1 = timeZone?.toString() },
- { "onTimeZoneChanged newTimeZone=$str1" }
- )
+ logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() }
}
@SuppressLint("DrawAllocation")
@@ -191,7 +189,7 @@ class AnimatableClockView @JvmOverloads constructor(
} else {
animator.updateLayout(layout)
}
- logBuffer?.log(TAG, DEBUG, "onMeasure")
+ logger?.d("onMeasure")
}
override fun onDraw(canvas: Canvas) {
@@ -203,12 +201,12 @@ class AnimatableClockView @JvmOverloads constructor(
} else {
super.onDraw(canvas)
}
- logBuffer?.log(TAG, DEBUG, "onDraw")
+ logger?.d("onDraw")
}
override fun invalidate() {
super.invalidate()
- logBuffer?.log(TAG, DEBUG, "invalidate")
+ logger?.d("invalidate")
}
override fun onTextChanged(
@@ -218,10 +216,7 @@ class AnimatableClockView @JvmOverloads constructor(
lengthAfter: Int
) {
super.onTextChanged(text, start, lengthBefore, lengthAfter)
- logBuffer?.log(TAG, DEBUG,
- { str1 = text.toString() },
- { "onTextChanged text=$str1" }
- )
+ logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() }
}
fun setLineSpacingScale(scale: Float) {
@@ -235,7 +230,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateColorChange() {
- logBuffer?.log(TAG, DEBUG, "animateColorChange")
+ logger?.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
textSize = -1f,
@@ -257,7 +252,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateAppearOnLockscreen() {
- logBuffer?.log(TAG, DEBUG, "animateAppearOnLockscreen")
+ logger?.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
textSize = -1f,
@@ -283,7 +278,7 @@ class AnimatableClockView @JvmOverloads constructor(
if (isAnimationEnabled && textAnimator == null) {
return
}
- logBuffer?.log(TAG, DEBUG, "animateFoldAppear")
+ logger?.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
textSize = -1f,
@@ -310,7 +305,7 @@ class AnimatableClockView @JvmOverloads constructor(
// Skip charge animation if dozing animation is already playing.
return
}
- logBuffer?.log(TAG, DEBUG, "animateCharge")
+ logger?.d("animateCharge")
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -334,7 +329,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateDoze(isDozing: Boolean, animate: Boolean) {
- logBuffer?.log(TAG, DEBUG, "animateDoze")
+ logger?.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
@@ -453,10 +448,7 @@ class AnimatableClockView @JvmOverloads constructor(
isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
else -> DOUBLE_LINE_FORMAT_12_HOUR
}
- logBuffer?.log(TAG, DEBUG,
- { str1 = format?.toString() },
- { "refreshFormat format=$str1" }
- )
+ logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() }
descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
refreshTime()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 14434655fc92..d65edae1caf0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -23,12 +23,13 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import androidx.annotation.OpenForTesting
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogMessage
import com.android.systemui.log.LogMessageImpl
-import com.android.systemui.log.MessageInitializer
-import com.android.systemui.log.MessagePrinter
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.log.core.MessageInitializer
+import com.android.systemui.log.core.MessagePrinter
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
@@ -75,7 +76,7 @@ private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
-private inline fun LogBuffer?.tryLog(
+private inline fun Logger?.tryLog(
tag: String,
level: LogLevel,
messageInitializer: MessageInitializer,
@@ -84,7 +85,7 @@ private inline fun LogBuffer?.tryLog(
) {
if (this != null) {
// Wrap messagePrinter to convert it from crossinline to noinline
- this.log(tag, level, messageInitializer, messagePrinter, ex)
+ this.log(level, messagePrinter, ex, messageInitializer)
} else {
messageInitializer(TMP_MESSAGE)
val msg = messagePrinter(TMP_MESSAGE)
@@ -110,7 +111,7 @@ open class ClockRegistry(
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
- val logBuffer: LogBuffer? = null,
+ messageBuffer: MessageBuffer? = null,
val keepAllLoaded: Boolean,
subTag: String,
var isTransitClockEnabled: Boolean = false,
@@ -124,6 +125,7 @@ open class ClockRegistry(
fun onAvailableClocksChanged() {}
}
+ private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null
private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
@@ -150,7 +152,7 @@ open class ClockRegistry(
val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
if (knownClocks == null) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = manager.getPackage() },
@@ -159,7 +161,7 @@ open class ClockRegistry(
return true
}
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = manager.getPackage() },
@@ -176,7 +178,7 @@ open class ClockRegistry(
}
if (manager != info.manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -216,7 +218,7 @@ open class ClockRegistry(
}
if (manager != info.manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -244,7 +246,7 @@ open class ClockRegistry(
val id = clock.clockId
val info = availableClocks[id]
if (info?.manager != manager) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = id },
@@ -319,7 +321,7 @@ open class ClockRegistry(
ClockSettings.deserialize(json)
} catch (ex: Exception) {
- logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
+ logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
null
}
settings = result
@@ -348,7 +350,7 @@ open class ClockRegistry(
)
}
} catch (ex: Exception) {
- logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
+ logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
}
settings = value
}
@@ -508,9 +510,9 @@ open class ClockRegistry(
}
private fun onConnected(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = clockId },
@@ -520,10 +522,10 @@ open class ClockRegistry(
}
private fun onLoaded(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.INFO,
{ str1 = clockId },
@@ -534,10 +536,10 @@ open class ClockRegistry(
}
private fun onUnloaded(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
@@ -548,10 +550,10 @@ open class ClockRegistry(
}
private fun onDisconnected(clockId: ClockId) {
- logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
+ logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
if (currentClockId == clockId) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
@@ -597,22 +599,17 @@ open class ClockRegistry(
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
- logBuffer.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = clockId },
- { "Rendering clock $str1" }
- )
+ logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" })
return clock
} else if (availableClocks.containsKey(clockId)) {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.WARNING,
{ str1 = clockId },
{ "Clock $str1 not loaded; using default" }
)
} else {
- logBuffer.tryLog(
+ logger.tryLog(
TAG,
LogLevel.ERROR,
{ str1 = clockId },
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index e557c8e5902a..e539c955a3c6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -24,7 +24,7 @@ import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockConfig
import com.android.systemui.plugins.ClockController
@@ -108,10 +108,10 @@ class DefaultClockController(
override val config = ClockFaceConfig()
- override var logBuffer: LogBuffer?
- get() = view.logBuffer
+ override var messageBuffer: MessageBuffer?
+ get() = view.messageBuffer
set(value) {
- view.logBuffer = value
+ view.messageBuffer = value
}
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
index 6fc525369e8b..a4f4e134733f 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
import com.google.errorprone.annotations.CompileTimeConstant
class ConstantStringsLoggerImpl(val buffer: LogBuffer, val tag: String) : ConstantStringsLogger {
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
index 2007e7606ab8..e0051f59469d 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
@@ -19,6 +19,11 @@ package com.android.systemui.log
import android.os.Trace
import android.util.Log
import com.android.systemui.common.buffer.RingBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.log.core.MessageInitializer
+import com.android.systemui.log.core.MessagePrinter
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.concurrent.ArrayBlockingQueue
@@ -73,7 +78,7 @@ constructor(
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
-) {
+) : MessageBuffer {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
private val echoMessageQueue: BlockingQueue<LogMessage>? =
@@ -174,11 +179,11 @@ constructor(
* store any relevant data on the message and then call [commit].
*/
@Synchronized
- fun obtain(
+ override fun obtain(
tag: String,
level: LogLevel,
messagePrinter: MessagePrinter,
- exception: Throwable? = null,
+ exception: Throwable?,
): LogMessage {
if (!mutable) {
return FROZEN_MESSAGE
@@ -195,7 +200,7 @@ constructor(
* have finished filling in its data fields. The message will be echoed to logcat if necessary.
*/
@Synchronized
- fun commit(message: LogMessage) {
+ override fun commit(message: LogMessage) {
if (!mutable) {
return
}
@@ -292,11 +297,5 @@ constructor(
}
}
-/**
- * A function that will be called immediately to store relevant data on the log message. The value
- * of `this` will be the LogMessage to be initialized.
- */
-typealias MessageInitializer = LogMessage.() -> Unit
-
private const val TAG = "LogBuffer"
private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
index 5e10f783850f..33cc199e7131 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
@@ -16,6 +16,10 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessagePrinter
+
/** Recyclable implementation of [LogMessage]. */
data class LogMessageImpl(
override var level: LogLevel,
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
index 55f3a738e4f1..ae717df50fce 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
@@ -16,6 +16,8 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+
/** Keeps track of which [LogBuffer] messages should also appear in logcat. */
interface LogcatEchoTracker {
/** Whether [bufferName] should echo messages of [level] or higher to logcat. */
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
index d0ad28f04670..9ff48cabc6f4 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
@@ -23,6 +23,7 @@ import android.os.Handler
import android.os.Looper
import android.os.Trace
import android.provider.Settings
+import com.android.systemui.log.core.LogLevel
/**
* Version of [LogcatEchoTracker] for debuggable builds
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
index 56966773d1b0..044d97f92b50 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
@@ -16,6 +16,8 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+
/** Production version of [LogcatEchoTracker] that isn't configurable. */
class LogcatEchoTrackerProd : LogcatEchoTracker {
override val logInBackgroundThread = false
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt
index 7d9647a13149..d30d8e9fe003 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log
+package com.android.systemui.log.core
import android.util.Log
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt
index 8c3988b027fe..3bd6473738c7 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log
+package com.android.systemui.log.core
import android.icu.text.SimpleDateFormat
import java.io.PrintWriter
@@ -67,6 +67,12 @@ interface LogMessage {
}
/**
+ * A function that will be called immediately to store relevant data on the log message. The value
+ * of `this` will be the LogMessage to be initialized.
+ */
+typealias MessageInitializer = LogMessage.() -> Unit
+
+/**
* A function that will be called if and when the message needs to be dumped to logcat or a bug
* report. It should read the data stored by the initializer and convert it to a human-readable
* string. The value of `this` will be the LogMessage to be printed. **IMPORTANT:** The printer
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
new file mode 100644
index 000000000000..5729ab270487
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+import com.google.errorprone.annotations.CompileTimeConstant
+
+/** Logs messages to the [MessageBuffer] with [tag]. */
+open class Logger(val buffer: MessageBuffer, val tag: String) {
+ /**
+ * Logs a message to the buffer.
+ *
+ * The actual string of the log message is not constructed until it is needed. To accomplish
+ * this, logging a message is a two-step process. First, a fresh instance of [LogMessage] is
+ * obtained and is passed to the [messageInitializer]. The initializer stores any relevant data
+ * on the message's fields. The message is then inserted into the buffer where it waits until it
+ * is either pushed out by newer messages or it needs to printed. If and when this latter moment
+ * occurs, the [messagePrinter] function is called on the message. It reads whatever data the
+ * initializer stored and converts it to a human-readable log message.
+ *
+ * @param level Which level to log the message at, both to the buffer and to logcat if it's
+ * echoed. In general, a module should split most of its logs into either INFO or DEBUG level.
+ * INFO level should be reserved for information that other parts of the system might care
+ * about, leaving the specifics of code's day-to-day operations to DEBUG.
+ * @param messagePrinter A function that will be called if and when the message needs to be
+ * dumped to logcat or a bug report. It should read the data stored by the initializer and
+ * convert it to a human-readable string. The value of `this` will be the [LogMessage] to be
+ * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the [LogMessage]
+ * and NEVER any variables in its enclosing scope. Otherwise, the runtime will need to
+ * allocate a new instance of the printer for each call, thwarting our attempts at avoiding
+ * any sort of allocation.
+ * @param exception Provide any exception that need to be logged. This is saved as
+ * [LogMessage.exception]
+ * @param messageInitializer A function that will be called immediately to store relevant data
+ * on the log message. The value of `this` will be the [LogMessage] to be initialized.
+ */
+ @JvmOverloads
+ inline fun log(
+ level: LogLevel,
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) {
+ val message = buffer.obtain(tag, level, messagePrinter, exception)
+ messageInitializer(message)
+ buffer.commit(message)
+ }
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer. Use sparingly.
+ *
+ * This is for simpler use-cases where [message] is a compile time string constant. For
+ * use-cases where the log message is built during runtime, use the [log] overloaded method that
+ * takes in an initializer and a message printer.
+ *
+ * Buffers are limited by the number of entries, so logging more frequently will limit the time
+ * window that the [MessageBuffer] covers in a bug report. Richer logs, on the other hand, make
+ * a bug report more actionable, so using the [log] with a [MessagePrinter] to add more details
+ * to every log may do more to improve overall logging than adding more logs with this method.
+ */
+ @JvmOverloads
+ fun log(
+ level: LogLevel,
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(level, { str1!! }, exception) { str1 = message }
+
+ /**
+ * Logs a message to the buffer at [LogLevel.VERBOSE].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun v(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.VERBOSE, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.VERBOSE]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun v(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.VERBOSE, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.DEBUG].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun d(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.DEBUG, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.DEBUG]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun d(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.DEBUG, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.INFO].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun i(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.INFO, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.INFO]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun i(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.INFO, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WARNING].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun w(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WARNING, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WARNING]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun w(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WARNING, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.ERROR].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun e(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.ERROR, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.ERROR]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun e(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.ERROR, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WTF].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun wtf(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WTF, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WTF]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun wtf(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WTF, message, exception)
+}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
new file mode 100644
index 000000000000..bb91633c4d87
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+/**
+ * [MessageBuffer] is an interface that represents a buffer of log messages, and provides methods to
+ * [obtain] a log message and [commit] it to the buffer.
+ */
+interface MessageBuffer {
+ /**
+ * Obtains the next [LogMessage] from the buffer.
+ *
+ * After calling [obtain], the caller must store any relevant data on the message and then call
+ * [commit].
+ */
+ fun obtain(
+ tag: String,
+ level: LogLevel,
+ messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ ): LogMessage
+
+ /**
+ * After acquiring a log message via [obtain], call this method to signal to the buffer that
+ * data fields have been filled.
+ */
+ fun commit(message: LogMessage)
+}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 1811c02d549d..64c0f99f4ba7 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -128,6 +128,16 @@ public interface BcSmartspaceDataPlugin extends Plugin {
void setDozeAmount(float amount);
/**
+ * Set if dozing is true or false
+ */
+ default void setDozing(boolean dozing) {}
+
+ /**
+ * Set if split shade enabled
+ */
+ default void setSplitShadeEnabled(boolean enabled) {}
+
+ /**
* Set the current keyguard bypass enabled status.
*/
default void setKeyguardBypassEnabled(boolean enabled) {}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 537b7a41a898..d962732ba884 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -18,7 +18,7 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import com.android.internal.annotations.Keep
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.annotations.ProvidesInterface
import java.io.PrintWriter
import java.util.Locale
@@ -95,7 +95,7 @@ interface ClockFaceController {
val animations: ClockAnimations
/** Some clocks may log debug information */
- var logBuffer: LogBuffer?
+ var messageBuffer: MessageBuffer?
}
/** Events that should call when various rendering parameters change */
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 02d55104c288..319481523cdf 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -62,15 +62,12 @@
-keep class ** extends androidx.preference.PreferenceFragment
-keep class com.android.systemui.tuner.*
-# The plugins, log & common subpackages act as shared libraries that might be referenced in
+# The plugins and core log subpackages act as shared libraries that might be referenced in
# dynamically-loaded plugin APKs.
-keep class com.android.systemui.plugins.** {
*;
}
--keep class com.android.systemui.log.** {
- *;
-}
--keep class com.android.systemui.common.** {
+-keep class com.android.systemui.log.core.** {
*;
}
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
diff --git a/core/res/res/values-watch/colors.xml b/packages/SystemUI/res-keyguard/color/shade_disabled.xml
index 6d908be48ff0..241f20385eb4 100644
--- a/core/res/res/values-watch/colors.xml
+++ b/packages/SystemUI/res-keyguard/color/shade_disabled.xml
@@ -1,12 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
- ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~ 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,
@@ -15,8 +14,6 @@
~ limitations under the License.
-->
-<resources>
- <!-- Wear Material standard colors -->
- <color name="wear_material_red_mid">#CC5D58</color>
- <color name="wear_material_grey_900">#202124</color>
-</resources> \ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="4" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
index a7ffe9ca256f..c09607d19bdd 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
android:layout_gravity="center"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?attr/onShadeInactiveVariant"
android:textSize="18sp"/>
<ImageView
android:id="@+id/new_dot"
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
index 6fe7d39f748a..1c31f1da0681 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
@@ -33,7 +33,7 @@
android:layout_marginEnd="12dp"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- android:tint="?android:attr/textColorSecondary" />
+ android:tint="?attr/onSurfaceVariant" />
<TextView
android:id="@+id/text"
@@ -43,7 +43,7 @@
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textColor="?attr/onSurfaceVariant"/>
<ImageView
android:id="@+id/new_dot"
@@ -62,5 +62,5 @@
android:contentDescription="@null"
android:src="@*android:drawable/ic_chevron_end"
android:autoMirrored="true"
- android:tint="?android:attr/textColorSecondary" />
+ android:tint="?attr/onSurfaceVariant" />
</com.android.systemui.animation.view.LaunchableLinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml b/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml
new file mode 100644
index 000000000000..360ef2672e75
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsKeyguardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
+
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index 209510365fe0..b5c181bd896c 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -15,6 +15,6 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?attr/underSurfaceColor" />
+ <solid android:color="?attr/underSurface" />
<corners android:radius="@dimen/rounded_slider_background_rounded_corner" />
</shape>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 569ee76586c2..95c7778c0e76 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -24,7 +24,7 @@
<shape>
<size android:height="@dimen/rounded_slider_track_width" />
<corners android:radius="@dimen/rounded_slider_track_corner_radius" />
- <solid android:color="?attr/offStateColor" />
+ <solid android:color="?attr/shadeInactive" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index 4d9188c40822..2ea90c717863 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -22,7 +22,7 @@
android:height="@dimen/rounded_slider_height">
<shape>
<size android:height="@dimen/rounded_slider_height" />
- <solid android:color="?priv-android:attr/colorAccentPrimary" />
+ <solid android:color="?attr/shadeActive" />
<corners android:radius="@dimen/rounded_slider_corner_radius"/>
</shape>
</item>
@@ -34,7 +34,7 @@
android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="?attr/onShadeActive"
/>
</item>
</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fgs_dot.xml b/packages/SystemUI/res/drawable/fgs_dot.xml
index 3669e1d3c374..0881d7c5c2b5 100644
--- a/packages/SystemUI/res/drawable/fgs_dot.xml
+++ b/packages/SystemUI/res/drawable/fgs_dot.xml
@@ -19,5 +19,5 @@
android:shape="oval"
android:width="12dp"
android:height="12dp">
- <solid android:color="?androidprv:attr/colorAccentTertiary" />
+ <solid android:color="?attr/tertiary" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/list_item_background.xml b/packages/SystemUI/res/drawable/list_item_background.xml
new file mode 100644
index 000000000000..2dbab9cfe984
--- /dev/null
+++ b/packages/SystemUI/res/drawable/list_item_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@*android:drawable/list_choice_background_material" />
+ <item android:drawable="@android:color/transparent" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
index ea0aafd321e1..e138d094f869 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
index ef950fe67ad2..f1a24aa7af9d 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -15,6 +15,6 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
index 14cb1de9fa2d..c4e45bf2c223 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -28,7 +28,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
- <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/materialColorPrimary"/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
android:top="@dimen/dialog_button_vertical_padding"
android:right="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
index 0544b871fa06..1590daa8b7f9 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -26,7 +26,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="18dp"/>
- <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryFixed"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index a47299d6f854..b0dc6523e971 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -29,7 +29,7 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="@android:color/transparent"/>
- <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ <stroke android:color="?androidprv:attr/materialColorPrimary"
android:width="1dp"
/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
index c8c36b0081c0..4a5d4af96497 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -28,7 +28,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?attr/offStateColor"/>
+ <solid android:color="?attr/shadeInactive"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index 6a365000a21c..a8c034986425 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -28,7 +28,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent"/>
+ <solid android:color="?attr/shadeActive"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
index c9517cd905dc..a7e8762a2593 100644
--- a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
<corners android:topLeftRadius="@dimen/qs_corner_radius"
android:topRightRadius="@dimen/qs_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
index 381af503d47c..0b0055b1f020 100644
--- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
@@ -29,7 +29,7 @@
<item>
<shape android:shape="rectangle">
<stroke android:width="1dp"
- android:color="?android:attr/colorBackground"/>
+ android:color="?attr/shadeInactive"/>
<corners android:radius="@dimen/qs_security_footer_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
index 88f13b451bbe..ca7df86d8296 100644
--- a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -42,7 +42,7 @@
android:layout_marginBottom="16dp"
android:scaleType="fitCenter"
android:src="@null"
- android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ android:tint="?androidprv:attr/materialColorPrimary"
/>
<TextView
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 3786812db827..fcf9638440c7 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -17,6 +17,8 @@
<com.android.systemui.statusbar.KeyboardShortcutAppItemLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
+ android:background="@drawable/list_item_background"
+ android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
@@ -55,6 +57,5 @@
android:layout_alignParentEnd="true"
android:textSize="14sp"
android:scrollHorizontally="false"
- android:layout_centerVertical="true"
- android:focusable="true"/>
+ android:layout_centerVertical="true"/>
</com.android.systemui.statusbar.KeyboardShortcutAppItemLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 8414223b7654..0759990f1677 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -16,10 +16,11 @@
~ limitations under the License
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="14sp"
android:fontFamily="sans-serif-medium"
+ android:importantForAccessibility="yes"
android:paddingStart="24dp"
android:paddingTop="20dp"
android:paddingEnd="24dp"
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 7105721aff70..21e0d2c0b8d7 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -164,7 +164,6 @@
/>
<ImageView
android:id="@+id/media_output_item_end_click_icon"
- android:src="@drawable/media_output_status_edit_session"
android:layout_width="24dp"
android:layout_height="24dp"
android:focusable="false"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 745cfc6c1655..b8f4c0f212c3 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -53,6 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
+ android:tint="?attr/shadeActive"
android:visibility="gone" />
<FrameLayout
@@ -70,7 +71,7 @@
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?attr/onSurfaceVariant" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index c124aea01afc..974cad32f937 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -54,6 +54,6 @@
android:focusable="false"
android:importantForAccessibility="no"
android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textColor="?attr/onShadeInactive"/>
</com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index bbf3adfb8c67..f6ce70d4d032 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -47,7 +48,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:text="@string/screenrecord_permission_dialog_title"
android:layout_marginTop="22dp"
@@ -56,8 +57,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
+ android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
android:gravity="center"
android:layout_marginBottom="20dp"/>
@@ -81,6 +81,7 @@
android:minHeight="48dp"
android:layout_weight="1"
android:popupBackground="@drawable/screenrecord_spinner_background"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:dropDownWidth="274dp"
android:prompt="@string/screenrecord_audio_label"/>
<Switch
@@ -117,7 +118,7 @@
android:text="@string/screenrecord_taps_label"
android:textAppearance="?android:attr/textAppearanceMedium"
android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:importantForAccessibility="no"/>
<Switch
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index ab522a388735..9af46c5b739c 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -36,14 +36,13 @@
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
android:src="@drawable/ic_media_projection_permission"
- android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ android:tint="?androidprv:attr/materialColorPrimary"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/screen_share_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
android:layout_marginTop="@dimen/screenrecord_title_margin_top"
android:gravity="center"/>
<Spinner
@@ -64,8 +63,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
+ style="@style/TextAppearance.Dialog.Body.Message"
android:gravity="start"
android:lineHeight="@dimen/screenrecord_warning_line_height"/>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
index 00af7f4e10e3..530d752732c1 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
@@ -16,8 +16,7 @@
-->
<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/udfps_animation_view"
+ android:id="@+id/udfps_animation_view_legacy"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3a1d1a8cbf9d..d693631080af 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -118,8 +118,25 @@
<attr name="wallpaperTextColorSecondary" format="reference|color" />
<attr name="wallpaperTextColorAccent" format="reference|color" />
<attr name="backgroundProtectedStyle" format="reference" />
- <attr name="offStateColor" format="reference|color" />
- <attr name="underSurfaceColor" format="reference|color" />
+
+ <!-- color attribute tokens for QS -->
+ <attr name="isQsTheme" format="boolean" />
+ <attr name="underSurface" format="reference|color"/>
+ <attr name="shadeActive" format="reference|color" />
+ <attr name="onShadeActive" format="reference|color" />
+ <attr name="onShadeActiveVariant" format="reference|color" />
+ <attr name="shadeInactive" format="reference|color" />
+ <attr name="onShadeInactive" format="reference|color" />
+ <attr name="onShadeInactiveVariant" format="reference|color" />
+ <attr name="shadeDisabled" format="reference|color" />
+ <attr name="surfaceBright" format="reference|color" />
+ <attr name="scHigh" format="reference|color" />
+ <attr name="tertiary" format="reference|color" />
+ <attr name="onSurface" format="reference|color" />
+ <attr name="onSurfaceVariant" format="reference|color" />
+ <attr name="outline" format="reference|color" />
+ <attr name="primary" format="reference|color" />
+
<declare-styleable name="SmartReplyView">
<attr name="spacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index cb5342a0d66b..fd74c7eae361 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -83,7 +83,7 @@
<style name="TextAppearance.QS">
<item name="android:textStyle">normal</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onShadeInactive</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
@@ -93,7 +93,7 @@
<style name="TextAppearance.QS.DetailItemSecondary">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">?attr/shadeActive</item>
</style>
<style name="TextAppearance.QS.Introduction">
@@ -117,11 +117,11 @@
<style name="TextAppearance.QS.DataUsage.Usage">
<item name="android:textSize">@dimen/qs_data_usage_usage_text_size</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">?attr/shadeActive</item>
</style>
<style name="TextAppearance.QS.DataUsage.Secondary">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onShadeInactiveVariant</item>
</style>
<style name="TextAppearance.QS.TileLabel">
@@ -137,31 +137,31 @@
<style name="TextAppearance.QS.UserSwitcher">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
</style>
<!-- This is hard coded to be sans-serif-condensed to match the icons -->
<style name="TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onSurface</item>
<item name="android:textSize">14sp</item>
<item name="android:letterSpacing">0.01</item>
</style>
<style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurface</item>
</style>
<style name="TextAppearance.QS.Status.Carriers" />
<style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="TextAppearance.QS.Status.Build">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
@@ -278,10 +278,10 @@
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
- <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
+ <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
</style>
- <style name="TextAppearance.DeviceManagementDialog.Content" parent="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
+ <style name="TextAppearance.DeviceManagementDialog.Content" parent="@style/TextAppearance.Dialog.Body.Message"/>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
@@ -371,14 +371,30 @@
</style>
<style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
+ <item name="isQsTheme">true</item>
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
- <item name="offStateColor">@color/material_dynamic_neutral20</item>
- <item name="underSurfaceColor">@color/material_dynamic_neutral0</item>
- <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+
+ <item name="surfaceBright">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="android:colorBackground">?attr/surfaceBright</item>
+ <item name="scHigh">?androidprv:attr/materialColorSurfaceContainerHigh</item>
+ <item name="primary">?androidprv:attr/materialColorPrimary</item>
+ <item name="tertiary">?androidprv:attr/materialColorTertiary</item>
+ <item name="onSurface">?androidprv:attr/materialColorOnSurface</item>
+ <item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="outline">?androidprv:attr/materialColorOutline</item>
+
+ <item name="shadeActive">@color/material_dynamic_primary90</item>
+ <item name="onShadeActive">@color/material_dynamic_primary10</item>
+ <item name="onShadeActiveVariant">@color/material_dynamic_primary30</item>
+ <item name="shadeInactive">@color/material_dynamic_neutral20</item>
+ <item name="onShadeInactive">@color/material_dynamic_neutral90</item>
+ <item name="onShadeInactiveVariant">@color/material_dynamic_neutral_variant80</item>
+ <item name="shadeDisabled">@color/shade_disabled</item>
+ <item name="underSurface">@color/material_dynamic_neutral0</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
@@ -390,8 +406,15 @@
<item name="android:windowBackground">@android:color/transparent</item>
</style>
- <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
+ <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@style/Theme.SystemUI.Dialog.QuickSettings">
+ </style>
+
+ <!-- Parent style overrides style in the dot inheritance -->
+ <style name="Theme.SystemUI.Dialog.QuickSettings" parent="@style/Theme.SystemUI.QuickSettings">
<item name="android:dialogCornerRadius">@dimen/notification_corner_radius</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
</style>
<!-- Overridden by values-television/styles.xml with tv-specific settings -->
@@ -406,7 +429,7 @@
<item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
<item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
- <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
+ <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceBright</item>
<item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
<item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
@@ -605,11 +628,11 @@
<item name="android:letterSpacing">0.01</item>
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onSurface</item>
<item name="android:elevation">10dp</item>
</style>
@@ -1055,7 +1078,7 @@
</style>
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:textSize">@dimen/dialog_title_text_size</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">32sp</item>
@@ -1064,7 +1087,7 @@
</style>
<style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">20sp</item>
@@ -1092,7 +1115,7 @@
<style name="Widget.Dialog.Button">
<item name="android:buttonCornerRadius">28dp</item>
<item name="android:background">@drawable/qs_dialog_btn_filled</item>
- <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
@@ -1102,12 +1125,18 @@
<style name="Widget.Dialog.Button.BorderButton">
<item name="android:background">@drawable/qs_dialog_btn_outline</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
</style>
<style name="Widget.Dialog.Button.Large">
<item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
<item name="android:minHeight">56dp</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryFixed</item>
+ </style>
+
+ <style name="Widget.Dialog.Button.QuickSettings">
+ <item name="android:textColor">?attr/primary</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 2e6c485336f3..751a3f8458bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -104,8 +104,7 @@ public class Utilities {
* @return updated set of flags from InputMethodService based off {@param oldHints}
* Leaves original hints unmodified
*/
- public static int calculateBackDispositionHints(int oldHints,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public static int calculateBackDispositionHints(int oldHints, int backDisposition,
boolean imeShown, boolean showImeSwitcher) {
int hints = oldHints;
switch (backDisposition) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 3c447a8eb6d8..ca064efd4f76 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -324,7 +324,7 @@ public class QuickStepContract {
* Returns whether the specified sysui state is such that the back gesture should be
* disabled.
*/
- public static boolean isBackGestureDisabled(int sysuiStateFlags) {
+ public static boolean isBackGestureDisabled(int sysuiStateFlags, boolean forTrackpad) {
// Always allow when the bouncer/global actions/voice session is showing (even on top of
// the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
@@ -335,16 +335,23 @@ public class QuickStepContract {
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
}
+
+ return (sysuiStateFlags & getBackGestureDisabledMask(forTrackpad)) != 0;
+ }
+
+ private static int getBackGestureDisabledMask(boolean forTrackpad) {
// Disable when in immersive, or the notifications are interactive
- int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ int disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ if (!forTrackpad) {
+ disableFlags |= SYSUI_STATE_NAV_BAR_HIDDEN;
+ }
// EdgeBackGestureHandler ignores Back gesture when SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED.
// To allow Shade to respond to Back, we're bypassing this check (behind a flag).
if (!ALLOW_BACK_GESTURE_IN_SHADE) {
disableFlags |= SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
}
-
- return (sysuiStateFlags & disableFlags) != 0;
+ return disableFlags;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 84a2c25999a0..4e0e8d0dbcd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -42,7 +42,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.KeyguardLargeClockLog
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController
@@ -91,9 +91,9 @@ constructor(
field = value
if (value != null) {
smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.smallClock.logBuffer = smallLogBuffer
+ value.smallClock.messageBuffer = smallLogBuffer
largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.largeClock.logBuffer = largeLogBuffer
+ value.largeClock.messageBuffer = largeLogBuffer
value.initialize(resources, dozeAmount, 0f)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 360f755623f7..4793b4f37eb3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -29,7 +29,7 @@ import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.shared.clocks.DefaultClockController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 84fc720f5a3b..21350727908c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -40,7 +40,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.KeyguardClockLog;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -352,6 +352,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+ mSmartspaceController.setSplitShadeEnabled(splitShadeEnabled);
+ }
+
+ /**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 6f596843bf9f..3e16d559742d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -43,15 +43,6 @@ public class KeyguardPinViewController
private long mPinLength;
private boolean mDisabledAutoConfirmation;
- /**
- * Responsible for identifying if PIN hinting is to be enabled or not
- */
- private boolean mIsPinHinting;
-
- /**
- * Responsible for identifying if auto confirm is enabled or not in Settings
- */
- private boolean mIsAutoPinConfirmEnabledInSettings;
protected KeyguardPinViewController(KeyguardPINView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -72,9 +63,6 @@ public class KeyguardPinViewController
mFeatureFlags = featureFlags;
mBackspaceKey = view.findViewById(R.id.delete_button);
mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
- mIsPinHinting = mPinLength == DEFAULT_PIN_LENGTH;
- mIsAutoPinConfirmEnabledInSettings = mLockPatternUtils.isAutoPinConfirmEnabled(
- KeyguardUpdateMonitor.getCurrentUser());
}
@Override
@@ -94,7 +82,7 @@ public class KeyguardPinViewController
protected void onUserInput() {
super.onUserInput();
- if (mIsAutoPinConfirmEnabledInSettings) {
+ if (isAutoPinConfirmEnabledInSettings()) {
updateAutoConfirmationState();
if (mPasswordEntry.getText().length() == mPinLength
&& mOkButton.getVisibility() == View.INVISIBLE) {
@@ -142,7 +130,7 @@ public class KeyguardPinViewController
* Updates the visibility of the OK button for auto confirm feature
*/
private void updateOKButtonVisibility() {
- if (mIsPinHinting && !mDisabledAutoConfirmation) {
+ if (isAutoPinConfirmEnabledInSettings() && !mDisabledAutoConfirmation) {
mOkButton.setVisibility(View.INVISIBLE);
} else {
mOkButton.setVisibility(View.VISIBLE);
@@ -154,9 +142,10 @@ public class KeyguardPinViewController
* Visibility changes are only for auto confirmation configuration.
*/
private void updateBackSpaceVisibility() {
+ boolean isAutoConfirmation = isAutoPinConfirmEnabledInSettings();
mBackspaceKey.setTransparentMode(/* isTransparentMode= */
- mIsAutoPinConfirmEnabledInSettings && !mDisabledAutoConfirmation);
- if (mIsAutoPinConfirmEnabledInSettings) {
+ isAutoConfirmation && !mDisabledAutoConfirmation);
+ if (isAutoConfirmation) {
if (mPasswordEntry.getText().length() > 0
|| mDisabledAutoConfirmation) {
mBackspaceKey.setVisibility(View.VISIBLE);
@@ -166,8 +155,24 @@ public class KeyguardPinViewController
}
}
/** Updates whether to use pin hinting or not. */
- private void updatePinHinting() {
- mPasswordEntry.setIsPinHinting(mIsAutoPinConfirmEnabledInSettings && mIsPinHinting
+ void updatePinHinting() {
+ mPasswordEntry.setIsPinHinting(isAutoPinConfirmEnabledInSettings() && isPinHinting()
&& !mDisabledAutoConfirmation);
}
+
+ /**
+ * Responsible for identifying if PIN hinting is to be enabled or not
+ */
+ private boolean isPinHinting() {
+ return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser())
+ == DEFAULT_PIN_LENGTH;
+ }
+
+ /**
+ * Responsible for identifying if auto confirm is enabled or not in Settings
+ */
+ private boolean isAutoPinConfirmEnabledInSettings() {
+ //Checks if user has enabled the auto confirm in Settings
+ return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 880f242c5938..458ca2b17c6d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -544,7 +544,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
if (mCancelAction != null) {
mCancelAction.run();
- mCancelAction = null;
}
mDismissAction = action;
mCancelAction = cancelAction;
@@ -782,9 +781,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
case SimPuk:
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
+ boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (securityMode == SecurityMode.None || isLockscreenDisabled) {
+ finish = isLockscreenDisabled;
eventSubtype = BOUNCER_DISMISS_SIM;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 00500d617766..6854c97c3415 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -323,6 +323,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean enabled) {
+ mKeyguardClockSwitchController.setSplitShadeEnabled(enabled);
+ }
+
+ /**
* Updates the alignment of the KeyguardStatusView and animates the transition if requested.
*/
public void updateAlignment(
@@ -350,6 +357,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
+ /* This transition blocks any layout changes while running. For that reason
+ * special logic with setting visibility was added to {@link BcSmartspaceView#setDozing}
+ * for split shade to avoid jump of the media object. */
ChangeBounds transition = new ChangeBounds();
if (splitShadeEnabled) {
// Excluding media from the transition on split-shade, as it doesn't transition
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 56e90bf73fe1..45744dfcab55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -24,6 +24,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_STOPPED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
@@ -251,6 +253,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
private static final int MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED = 346;
private static final int MSG_SERVICE_PROVIDERS_UPDATED = 347;
+ private static final int MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED = 348;
/** Biometric authentication state: Not listening. */
private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -300,6 +303,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
+ private static final List<Integer> ABSENT_SIM_STATE_LIST = Arrays.asList(
+ TelephonyManager.SIM_STATE_ABSENT,
+ TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_NOT_READY);
+
private final Context mContext;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
@@ -2484,6 +2492,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED:
handleKeyguardDismissAnimationFinished();
break;
+ case MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED:
+ notifyAboutEnrollmentChange(msg.arg1);
+ break;
default:
super.handleMessage(msg);
break;
@@ -2584,6 +2595,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0)
+ .sendToTarget();
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
}
@@ -3433,6 +3446,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return mIsFaceEnrolled;
}
+ private void notifyAboutEnrollmentChange(@BiometricAuthenticator.Modality int modality) {
+ BiometricSourceType biometricSourceType;
+ if (modality == TYPE_FINGERPRINT) {
+ biometricSourceType = FINGERPRINT;
+ } else if (modality == TYPE_FACE) {
+ biometricSourceType = FACE;
+ } else {
+ return;
+ }
+ mLogger.notifyAboutEnrollmentsChanged(biometricSourceType);
+ Assert.isMainThread();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricEnrollmentStateChanged(biometricSourceType);
+ }
+ }
+ }
+
private void stopListeningForFingerprint() {
mLogger.v("stopListeningForFingerprint()");
if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING) {
@@ -3715,8 +3747,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.logSimState(subId, slotId, state);
boolean becameAbsent = false;
- if (!SubscriptionManager.isValidSubscriptionId(subId)
- && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
mLogger.w("invalid subId in handleSimStateChange()");
/* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
* handleServiceStateChange() handle other case */
@@ -3734,11 +3765,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
} else if (state == TelephonyManager.SIM_STATE_CARD_IO_ERROR) {
updateTelephonyCapable(true);
- } else {
- return;
}
}
+ becameAbsent |= ABSENT_SIM_STATE_LIST.contains(state);
+
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
@@ -3751,7 +3782,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
data.subId = subId;
data.slotId = slotId;
}
- if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) {
+ if ((changed || becameAbsent)) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 73940055c89f..7b596328ca13 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -327,4 +327,9 @@ public class KeyguardUpdateMonitorCallback {
* Called when the enabled trust agents associated with the specified user.
*/
public void onEnabledTrustAgentsChanged(int userId) { }
+
+ /**
+ * On biometric enrollment state changed
+ */
+ public void onBiometricEnrollmentStateChanged(BiometricSourceType biometricSourceType) { }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 61af7228fe0d..71f78c32317c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -23,7 +23,7 @@ import android.view.View;
import com.android.app.animation.Interpolators;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index d1fffaa926ea..76b073ef73af 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -32,7 +32,6 @@ import android.widget.ImageView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
@@ -126,7 +125,6 @@ public class LockIconView extends FrameLayout implements Dumpable {
/**
* Set the location of the lock icon.
*/
- @VisibleForTesting
public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
mLockIconCenter = center;
mRadius = radius;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 239a0cc01c45..e255f5c2ded6 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,6 +60,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -387,15 +388,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (mUdfpsSupported) {
- mView.setCenterLocation(mAuthController.getUdfpsLocation(),
- mAuthController.getUdfpsRadius(), scaledPadding);
- } else {
- mView.setCenterLocation(
- new Point((int) mWidthPixels / 2,
- (int) (mHeightPixels
- - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (mUdfpsSupported) {
+ mView.setCenterLocation(mAuthController.getUdfpsLocation(),
+ mAuthController.getUdfpsRadius(), scaledPadding);
+ } else {
+ mView.setCenterLocation(
+ new Point((int) mWidthPixels / 2,
+ (int) (mHeightPixels
+ - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
sLockIconRadiusPx * scaleFactor, scaledPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
index e7295ef9052c..54738c62646a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -18,7 +18,7 @@ package com.android.keyguard.logging
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.BiometricLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
index c00b2c612fa2..cfad614a460d 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
@@ -19,9 +19,9 @@ package com.android.keyguard.logging
import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
index 19787154bec4..d02b72f37795 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
@@ -19,7 +19,7 @@ package com.android.keyguard.logging
import androidx.annotation.IntDef
import com.android.keyguard.CarrierTextManager.CarrierTextCallbackInfo
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.CarrierTextManagerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 8b925b1bfb54..bddf3b07dbb5 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -19,7 +19,7 @@ package com.android.keyguard.logging
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.KeyguardLog
import com.android.systemui.statusbar.KeyguardIndicationController
import com.google.errorprone.annotations.CompileTimeConstant
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 18ba09a63221..59e0cadec9b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -18,6 +18,7 @@ package com.android.keyguard.logging
import android.content.Intent
import android.hardware.biometrics.BiometricConstants.LockoutMode
+import android.hardware.biometrics.BiometricSourceType
import android.os.PowerManager
import android.os.PowerManager.WakeReason
import android.telephony.ServiceState
@@ -32,12 +33,12 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.TrustGrantFlags
import com.android.settingslib.fuelgauge.BatteryStatus
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-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.VERBOSE
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
@@ -370,16 +371,14 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
fun logServiceProvidersUpdated(intent: Intent) {
logBuffer.log(
- TAG,
- VERBOSE,
- {
- int1 = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID)
- str1 = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
- str2 = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
- },
- {
- "action SERVICE_PROVIDERS_UPDATED subId=$int1 spn=$str1 plmn=$str2"
- }
+ TAG,
+ VERBOSE,
+ {
+ int1 = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID)
+ str1 = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+ str2 = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
+ },
+ { "action SERVICE_PROVIDERS_UPDATED subId=$int1 spn=$str1 plmn=$str2" }
)
}
@@ -719,4 +718,13 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
fun scheduleWatchdog(@CompileTimeConstant watchdogType: String) {
logBuffer.log(TAG, DEBUG, "Scheduling biometric watchdog for $watchdogType")
}
+
+ fun notifyAboutEnrollmentsChanged(biometricSourceType: BiometricSourceType) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = "$biometricSourceType" },
+ { "notifying about enrollments changed: $str1" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
index cb764a8f94aa..57b5db15de67 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
@@ -21,8 +21,8 @@ import com.android.systemui.keyguard.shared.model.ActiveUnlockModel
import com.android.systemui.keyguard.shared.model.TrustManagedModel
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
index 670c1fa45e5c..c46558532372 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -56,9 +56,14 @@ class ScreenDecorHwcLayer(
) : DisplayCutoutBaseView(context) {
val colorMode: Int
private val useInvertedAlphaColor: Boolean
- private val color: Int
+ private var color: Int = Color.BLACK
+ set(value) {
+ field = value
+ paint.color = value
+ }
+
private val bgColor: Int
- private val cornerFilter: ColorFilter
+ private var cornerFilter: ColorFilter
private val cornerBgFilter: ColorFilter
private val clearPaint: Paint
@JvmField val transparentRect: Rect = Rect()
@@ -109,10 +114,16 @@ class ScreenDecorHwcLayer(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
parent.requestTransparentRegion(this)
+ updateColors()
+ }
+
+ private fun updateColors() {
if (!debug) {
viewRootImpl.setDisplayDecoration(true)
}
+ cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
+
if (useInvertedAlphaColor) {
paint.set(clearPaint)
} else {
@@ -121,6 +132,21 @@ class ScreenDecorHwcLayer(
}
}
+ fun setDebugColor(color: Int) {
+ if (!debug) {
+ return
+ }
+
+ if (this.color == color) {
+ return
+ }
+
+ this.color = color
+
+ updateColors()
+ invalidate()
+ }
+
override fun onUpdate() {
parent.requestTransparentRegion(this)
}
@@ -367,7 +393,7 @@ class ScreenDecorHwcLayer(
/**
* Update the rounded corner drawables.
*/
- fun updateRoundedCornerDrawable(top: Drawable, bottom: Drawable) {
+ fun updateRoundedCornerDrawable(top: Drawable?, bottom: Drawable?) {
roundedCornerDrawableTop = top
roundedCornerDrawableBottom = bottom
updateRoundedCornerDrawableBounds()
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 67d4a2e25051..de7a66900355 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -69,9 +69,9 @@ import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.DebugRoundedCornerDelegate;
+import com.android.systemui.decor.DebugRoundedCornerModel;
import com.android.systemui.decor.DecorProvider;
import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.DecorProviderKt;
@@ -80,10 +80,12 @@ import com.android.systemui.decor.OverlayWindow;
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegateImpl;
+import com.android.systemui.decor.ScreenDecorCommand;
import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
@@ -95,7 +97,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -130,7 +131,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
@VisibleForTesting
protected boolean mIsRegistered;
private final Context mContext;
- private final Executor mMainExecutor;
+ private final CommandRegistry mCommandRegistry;
private final SecureSettings mSecureSettings;
@VisibleForTesting
DisplayTracker.Callback mDisplayListener;
@@ -313,8 +314,8 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
@Inject
public ScreenDecorations(Context context,
- @Main Executor mainExecutor,
SecureSettings secureSettings,
+ CommandRegistry commandRegistry,
UserTracker userTracker,
DisplayTracker displayTracker,
PrivacyDotViewController dotViewController,
@@ -324,8 +325,8 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
ScreenDecorationsLogger logger,
AuthController authController) {
mContext = context;
- mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
+ mCommandRegistry = commandRegistry;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mDotViewController = dotViewController;
@@ -350,6 +351,45 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
}
};
+ private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
+ // If we are exiting debug mode, we can set it (false) and bail, otherwise we will
+ // ensure that debug mode is set
+ if (cmd.getDebug() != null && !cmd.getDebug()) {
+ setDebug(false);
+ return;
+ } else {
+ // setDebug is idempotent
+ setDebug(true);
+ }
+
+ if (cmd.getColor() != null) {
+ mDebugColor = cmd.getColor();
+ mExecutor.execute(() -> {
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.setDebugColor(cmd.getColor());
+ }
+ updateColorInversionDefault();
+ });
+ }
+
+ DebugRoundedCornerModel roundedTop = null;
+ DebugRoundedCornerModel roundedBottom = null;
+ if (cmd.getRoundedTop() != null) {
+ roundedTop = cmd.getRoundedTop().toRoundedCornerDebugModel();
+ }
+ if (cmd.getRoundedBottom() != null) {
+ roundedBottom = cmd.getRoundedBottom().toRoundedCornerDebugModel();
+ }
+ if (roundedTop != null || roundedBottom != null) {
+ mDebugRoundedCornerDelegate.applyNewDebugCorners(roundedTop, roundedBottom);
+ mExecutor.execute(() -> {
+ removeAllOverlays();
+ removeHwcOverlay();
+ setupDecorations();
+ });
+ }
+ };
+
@Override
public void start() {
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -361,6 +401,8 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
mExecutor.execute(this::startOnScreenDecorationsThread);
mDotViewController.setUiExecutor(mExecutor);
mAuthController.addCallback(mAuthControllerCallback);
+ mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
+ () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
}
/**
@@ -1228,7 +1270,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
bottomDrawable = mDebugRoundedCornerDelegate.getBottomRoundedDrawable();
}
- if (topDrawable == null || bottomDrawable == null) {
+ if (topDrawable == null && bottomDrawable == null) {
return;
}
mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable);
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 45077d2333b6..6f99a24b7312 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -51,7 +51,11 @@ public class SlicePermissionActivity extends Activity implements OnClickListener
super.onCreate(savedInstanceState);
// Verify intent is valid
- mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI);
+ try {
+ mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to getParcelableExtra", e);
+ }
mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG);
if (mUri == null
|| !SliceProvider.SLICE_TYPE.equals(getContentResolver().getType(mUri))
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6f0f6331ef50..946ddba6a341 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,7 +37,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index dc9ba8719717..ebff0b05a52d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -85,6 +85,8 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -153,6 +155,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ @NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@@ -272,7 +275,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils,
- mUdfpsKeyguardAccessibilityDelegate)));
+ mUdfpsKeyguardAccessibilityDelegate,
+ mUdfpsKeyguardViewModels)));
}
@Override
@@ -591,6 +595,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// Pilfer if valid overlap, don't allow following events to reach keyguard
shouldPilfer = true;
+
+ // Touch is a valid UDFPS touch. Inform the falsing manager so that the touch
+ // isn't counted against the falsing algorithm as an accidental touch.
+ // We do this on the DOWN event instead of CANCEL/UP because the CANCEL/UP events
+ // get sent too late to this receiver (after the actual cancel/up motions occur),
+ // and therefore wouldn't end up being used as part of the falsing algo.
+ mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UP:
@@ -610,7 +621,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
data.getTime(),
data.getGestureStart(),
mStatusBarStateController.isDozing());
- mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UNCHANGED:
@@ -784,7 +794,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
private boolean shouldTryToDismissKeyguard() {
return mOverlay != null
&& mOverlay.getAnimationViewController()
- instanceof UdfpsKeyguardViewControllerLegacy
+ instanceof UdfpsKeyguardViewControllerAdapter
&& mKeyguardStateController.canDismissLockScreen()
&& !mAttemptedToDismissKeyguard;
}
@@ -829,7 +839,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull InputManager inputManager,
@NonNull UdfpsUtils udfpsUtils,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
- @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate) {
+ @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
+ @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -895,6 +906,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return Unit.INSTANCE;
});
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mUdfpsKeyguardViewModels = udfpsKeyguardViewModelsProvider;
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index e5421471931f..d6ef94d18e71 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,15 +46,19 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.settingslib.udfps.UdfpsUtils
import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.settingslib.udfps.UdfpsUtils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -64,6 +68,8 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import javax.inject.Provider
private const val TAG = "UdfpsControllerOverlay"
@@ -75,6 +81,7 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
* request. This state can persist across configuration changes via the [show] and [hide]
* methods.
*/
+@ExperimentalCoroutinesApi
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
private val context: Context,
@@ -105,6 +112,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
private val udfpsUtils: UdfpsUtils,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
) {
/** The view, when [isShowing], or null. */
var overlayView: UdfpsView? = null
@@ -243,27 +251,40 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
)
}
REASON_AUTH_KEYGUARD -> {
- UdfpsKeyguardViewControllerLegacy(
- view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
- updateSensorLocation(sensorBounds)
- },
- statusBarStateController,
- shadeExpansionStateManager,
- statusBarKeyguardViewManager,
- keyguardUpdateMonitor,
- dumpManager,
- transitionController,
- configurationController,
- keyguardStateController,
- unlockedScreenOffAnimationController,
- dialogManager,
- controller,
- activityLaunchAnimator,
- featureFlags,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- udfpsKeyguardAccessibilityDelegate,
- )
+ if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ udfpsKeyguardViewModels.get().setSensorBounds(sensorBounds)
+ UdfpsKeyguardViewController(
+ view.addUdfpsView(R.layout.udfps_keyguard_view),
+ statusBarStateController,
+ shadeExpansionStateManager,
+ dialogManager,
+ dumpManager,
+ alternateBouncerInteractor,
+ udfpsKeyguardViewModels.get(),
+ )
+ } else {
+ UdfpsKeyguardViewControllerLegacy(
+ view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
+ updateSensorLocation(sensorBounds)
+ },
+ statusBarStateController,
+ shadeExpansionStateManager,
+ statusBarKeyguardViewManager,
+ keyguardUpdateMonitor,
+ dumpManager,
+ transitionController,
+ configurationController,
+ keyguardStateController,
+ unlockedScreenOffAnimationController,
+ dialogManager,
+ controller,
+ activityLaunchAnimator,
+ featureFlags,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ udfpsKeyguardAccessibilityDelegate,
+ )
+ }
}
REASON_AUTH_BP -> {
// note: empty controller, currently shows no visual affordance
@@ -415,7 +436,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
}
private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
- if (animation !is UdfpsKeyguardViewControllerLegacy) {
+ if (animation !is UdfpsKeyguardViewControllerAdapter) {
// always rotate view if we're not on the keyguard
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
new file mode 100644
index 000000000000..8cc15dadffd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.util.AttributeSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** View corresponding with udfps_keyguard_view.xml */
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardView(
+ context: Context,
+ attrs: AttributeSet?,
+) :
+ UdfpsAnimationView(
+ context,
+ attrs,
+ ) {
+ private val fingerprintDrawablePlaceHolder = UdfpsFpDrawable(context)
+ private var visible = false
+
+ override fun calculateAlpha(): Int {
+ return if (mPauseAuth) {
+ 0
+ } else 255 // ViewModels handle animating alpha values
+ }
+
+ override fun getDrawable(): UdfpsDrawable {
+ return fingerprintDrawablePlaceHolder
+ }
+
+ fun useExpandedOverlay(useExpandedOverlay: Boolean) {
+ mUseExpandedOverlay = useExpandedOverlay
+ }
+
+ fun isVisible(): Boolean {
+ return visible
+ }
+
+ fun setVisible(isVisible: Boolean) {
+ visible = isVisible
+ isPauseAuth = !visible
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9bafeeca24a8..15bd73193687 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -34,6 +34,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
@@ -80,7 +81,8 @@ constructor(
shadeExpansionStateManager,
systemUIDialogManager,
dumpManager,
- ) {
+ ),
+ UdfpsKeyguardViewControllerAdapter {
private val useExpandedOverlay: Boolean =
featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
private var showingUdfpsBouncer = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
index 39199d194cc9..2102a1f72be2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
@@ -17,10 +17,10 @@
package com.android.systemui.biometrics
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.VERBOSE
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.dagger.UdfpsLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
new file mode 100644
index 000000000000..2a9f3eafc776
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.controller
+
+import com.android.systemui.biometrics.UdfpsAnimationViewController
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Class that coordinates non-HBM animations during keyguard authentication. */
+@ExperimentalCoroutinesApi
+open class UdfpsKeyguardViewController(
+ val view: UdfpsKeyguardView,
+ statusBarStateController: StatusBarStateController,
+ shadeExpansionStateManager: ShadeExpansionStateManager,
+ systemUIDialogManager: SystemUIDialogManager,
+ dumpManager: DumpManager,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ udfpsKeyguardViewModels: UdfpsKeyguardViewModels,
+) :
+ UdfpsAnimationViewController<UdfpsKeyguardView>(
+ view,
+ statusBarStateController,
+ shadeExpansionStateManager,
+ systemUIDialogManager,
+ dumpManager,
+ ),
+ UdfpsKeyguardViewControllerAdapter {
+ override val tag: String
+ get() = TAG
+
+ init {
+ udfpsKeyguardViewModels.bindViews(view)
+ }
+
+ public override fun onViewAttached() {
+ super.onViewAttached()
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
+ }
+
+ public override fun onViewDetached() {
+ super.onViewDetached()
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(false)
+ }
+
+ override fun shouldPauseAuth(): Boolean {
+ return !view.isVisible()
+ }
+
+ override fun listenForTouchesOutsideView(): Boolean {
+ return true
+ }
+
+ companion object {
+ private const val TAG = "UdfpsKeyguardViewController"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
index 96af42bfda22..2a457ebd62f7 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.bluetooth
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.BluetoothLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
index 5b3a982ab5e2..068f32986cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
@@ -21,10 +21,10 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogMessage
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 76002d3f9693..40db63d609ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@ import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
+import com.android.systemui.statusbar.phone.LockscreenWallpaper
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
@@ -301,4 +302,9 @@ abstract class SystemUICoreStartableModule {
@IntoMap
@ClassKey(KeyguardViewConfigurator::class)
abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(LockscreenWallpaper::class)
+ abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f229ffe9d0dc..06769dc3f831 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -53,6 +53,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
+import com.android.systemui.keyguard.ui.view.layout.LockscreenLayoutModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -177,6 +178,7 @@ import javax.inject.Named;
GarbageMonitorModule.class,
KeyboardModule.class,
LetterboxModule.class,
+ LockscreenLayoutModule.class,
LogModule.class,
MediaProjectionModule.class,
MotionToolModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
index 4069bc7d73d0..557168731b23 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
@@ -77,16 +77,30 @@ class DebugRoundedCornerDelegate : RoundedCornerResDelegate {
}
fun applyNewDebugCorners(
- topCorner: DebugRoundedCornerModel,
- bottomCorner: DebugRoundedCornerModel,
+ topCorner: DebugRoundedCornerModel?,
+ bottomCorner: DebugRoundedCornerModel?,
) {
- hasTop = true
- topRoundedDrawable = topCorner.toPathDrawable(paint)
- topRoundedSize = topCorner.size()
+ topCorner?.let {
+ hasTop = true
+ topRoundedDrawable = it.toPathDrawable(paint)
+ topRoundedSize = it.size()
+ }
+ ?: {
+ hasTop = false
+ topRoundedDrawable = null
+ topRoundedSize = Size(0, 0)
+ }
- hasBottom = true
- bottomRoundedDrawable = bottomCorner.toPathDrawable(paint)
- bottomRoundedSize = bottomCorner.size()
+ bottomCorner?.let {
+ hasBottom = true
+ bottomRoundedDrawable = it.toPathDrawable(paint)
+ bottomRoundedSize = it.size()
+ }
+ ?: {
+ hasBottom = false
+ bottomRoundedDrawable = null
+ bottomRoundedSize = Size(0, 0)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt b/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt
new file mode 100644
index 000000000000..fa1d898de850
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.decor
+
+import android.graphics.Color
+import android.graphics.Path
+import android.util.PathParser
+import com.android.systemui.statusbar.commandline.ParseableCommand
+import com.android.systemui.statusbar.commandline.Type
+import com.android.systemui.statusbar.commandline.map
+import java.io.PrintWriter
+
+/** Debug screen-decor command to be handled by the SystemUI command line interface */
+class ScreenDecorCommand(
+ private val callback: Callback,
+) : ParseableCommand(SCREEN_DECOR_CMD_NAME) {
+ val debug: Boolean? by
+ param(
+ longName = "debug",
+ description =
+ "Enter or exits debug mode. Effectively makes the corners visible and allows " +
+ "for overriding the path data for the anti-aliasing corner paths and display " +
+ "cutout.",
+ valueParser = Type.Boolean,
+ )
+
+ val color: Int? by
+ param(
+ longName = "color",
+ shortName = "c",
+ description =
+ "Set a specific color for the debug assets. See Color#parseString() for " +
+ "accepted inputs.",
+ valueParser = Type.String.map { it.toColorIntOrNull() }
+ )
+
+ val roundedTop: RoundedCornerSubCommand? by subCommand(RoundedCornerSubCommand("rounded-top"))
+
+ val roundedBottom: RoundedCornerSubCommand? by
+ subCommand(RoundedCornerSubCommand("rounded-bottom"))
+
+ override fun execute(pw: PrintWriter) {
+ callback.onExecute(this, pw)
+ }
+
+ override fun toString(): String {
+ return "ScreenDecorCommand(" +
+ "debug=$debug, " +
+ "color=$color, " +
+ "roundedTop=$roundedTop, " +
+ "roundedBottom=$roundedBottom)"
+ }
+
+ /** For use in ScreenDecorations.java, define a Callback */
+ interface Callback {
+ fun onExecute(cmd: ScreenDecorCommand, pw: PrintWriter)
+ }
+
+ companion object {
+ const val SCREEN_DECOR_CMD_NAME = "screen-decor"
+ }
+}
+
+/**
+ * Defines a subcommand suitable for `rounded-top` and `rounded-bottom`. They both have the same
+ * API.
+ */
+class RoundedCornerSubCommand(name: String) : ParseableCommand(name) {
+ val height by
+ param(
+ longName = "height",
+ description = "The height of a corner, in pixels.",
+ valueParser = Type.Int,
+ )
+ .required()
+
+ val width by
+ param(
+ longName = "width",
+ description =
+ "The width of the corner, in pixels. Likely should be equal to the height.",
+ valueParser = Type.Int,
+ )
+ .required()
+
+ val pathData by
+ param(
+ longName = "path-data",
+ shortName = "d",
+ description =
+ "PathParser-compatible path string to be rendered as the corner drawable. " +
+ "This path should be a closed arc oriented as the top-left corner " +
+ "of the device",
+ valueParser = Type.String.map { it.toPathOrNull() }
+ )
+ .required()
+
+ val viewportHeight: Float? by
+ param(
+ longName = "viewport-height",
+ description =
+ "The height of the viewport for the given path string. " +
+ "If null, the corner height will be used.",
+ valueParser = Type.Float,
+ )
+
+ val scaleY: Float
+ get() = viewportHeight?.let { height.toFloat() / it } ?: 1.0f
+
+ val viewportWidth: Float? by
+ param(
+ longName = "viewport-width",
+ description =
+ "The width of the viewport for the given path string. " +
+ "If null, the corner width will be used.",
+ valueParser = Type.Float,
+ )
+
+ val scaleX: Float
+ get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f
+
+ override fun execute(pw: PrintWriter) {
+ // Not needed for a subcommand
+ }
+
+ override fun toString(): String {
+ return "RoundedCornerSubCommand(" +
+ "height=$height," +
+ " width=$width," +
+ " pathData='$pathData'," +
+ " viewportHeight=$viewportHeight," +
+ " viewportWidth=$viewportWidth)"
+ }
+
+ fun toRoundedCornerDebugModel(): DebugRoundedCornerModel =
+ DebugRoundedCornerModel(
+ path = pathData,
+ width = width,
+ height = height,
+ scaleX = scaleX,
+ scaleY = scaleY,
+ )
+}
+
+fun String.toPathOrNull(): Path? =
+ try {
+ PathParser.createPathFromPathData(this)
+ } catch (e: Exception) {
+ null
+ }
+
+fun String.toColorIntOrNull(): Int? =
+ try {
+ Color.parseColor(this)
+ } catch (e: Exception) {
+ null
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
index c962e5155697..bcfeeb9eaadd 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
@@ -23,7 +23,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.DisplayMetricsRepoLog
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 536978009f71..75b8e513c14a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -20,9 +20,9 @@ import android.view.Display
import com.android.systemui.doze.DozeLog.Reason
import com.android.systemui.doze.DozeLog.reasonToString
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.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.DozeLog
import com.android.systemui.statusbar.policy.DevicePostureController
import com.google.errorprone.annotations.CompileTimeConstant
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
index fdb765130157..0e224060a36f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.dreams
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.DreamLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4aa9080cb7f5..68211eede476 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -91,6 +91,15 @@ object Flags {
val NOTIFICATION_SHELF_REFACTOR =
unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true)
+ // TODO(b/288326013): Tracking Bug
+ @JvmField
+ val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
+ unreleasedFlag(
+ 288326013,
+ "notification_async_hybrid_view_inflation",
+ teamfood = false
+ )
+
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
releasedFlag(270682168, "animated_notification_shade_insets")
@@ -251,11 +260,6 @@ object Flags {
@JvmField
val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
- /** Migrate the lock icon view to the new keyguard root view. */
- // TODO(b/286552209): Tracking bug.
- @JvmField
- val MIGRATE_LOCK_ICON = unreleasedFlag(238, "migrate_lock_icon")
-
/** Whether to listen for fingerprint authentication over keyguard occluding activities. */
// TODO(b/283260512): Tracking bug.
@JvmField
@@ -270,6 +274,11 @@ object Flags {
@JvmField
val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock")
+ /** Migrate the lock icon view to the new keyguard root view. */
+ // TODO(b/286552209): Tracking bug.
+ @JvmField
+ val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -308,7 +317,7 @@ object Flags {
)
@JvmField
- val QS_PIPELINE_NEW_HOST = releasedFlag(504, "qs_pipeline_new_host")
+ val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = true)
// TODO(b/278068252): Tracking Bug
@JvmField
@@ -669,13 +678,11 @@ object Flags {
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
@JvmField
- val ENABLE_STYLUS_CHARGING_UI =
- unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
+ val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui")
@JvmField
val ENABLE_USI_BATTERY_NOTIFICATIONS =
- unreleasedFlag(2302, "enable_usi_battery_notifications", teamfood = true)
- @JvmField val ENABLE_STYLUS_EDUCATION =
- unreleasedFlag(2303, "enable_stylus_education", teamfood = true)
+ releasedFlag(2302, "enable_usi_battery_notifications")
+ @JvmField val ENABLE_STYLUS_EDUCATION = releasedFlag(2303, "enable_stylus_education")
// 2400 - performance tools and debugging info
// TODO(b/238923086): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e8881a482765..f59ad90d86ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,6 +26,8 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -42,6 +44,8 @@ constructor(
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
+ private val keyguardLayoutManager: KeyguardLayoutManager,
+ private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
) : CoreStartable {
private var indicationAreaHandle: DisposableHandle? = null
@@ -51,6 +55,8 @@ constructor(
notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
bindIndicationArea(notificationPanel)
bindLockIconView(notificationPanel)
+ keyguardLayoutManager.layoutViews()
+ keyguardLayoutManagerCommandListener.start()
}
fun bindIndicationArea(legacyParent: ViewGroup) {
@@ -59,7 +65,7 @@ constructor(
// At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
// Disable one of them
if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
- legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let {
+ legacyParent.findViewById<View>(R.id.keyguard_indication_area)?.let {
legacyParent.removeView(it)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 341c34e38db9..9a32e94180c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -140,6 +140,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -545,6 +546,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private CentralSurfaces mCentralSurfaces;
+ private IRemoteAnimationFinishedCallback mUnoccludeFromDreamFinishedCallback;
+
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -582,17 +585,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onUserSwitching(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
- // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
- // We need to force a reset of the views, since lockNow (called by
- // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
- if (mLockPatternUtils.isLockScreenDisabled(userId)) {
- // If we are switching to a user that has keyguard disabled, dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- } else {
- resetStateLocked();
- }
+ dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
}
}
@@ -600,16 +595,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onUserSwitchComplete(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
- if (userId != UserHandle.USER_SYSTEM) {
- UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- // Don't try to dismiss if the user has Pin/Pattern/Password set
- if (info == null || mLockPatternUtils.isSecure(userId)) {
- return;
- } else if (info.isGuest() || info.isDemo()) {
- // If we just switched to a guest, try to dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- }
- }
+ // We are calling dismiss again and with a delay as there are race conditions
+ // in some scenarios caused by async layout listeners
+ mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500);
}
@Override
@@ -649,6 +637,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
switch (simState) {
case TelephonyManager.SIM_STATE_NOT_READY:
case TelephonyManager.SIM_STATE_ABSENT:
+ case TelephonyManager.SIM_STATE_UNKNOWN:
+ mPendingPinLock = false;
// only force lock screen in case of missing sim if user hasn't
// gone through setup wizard
synchronized (KeyguardViewMediator.this) {
@@ -713,9 +703,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
}
break;
- case TelephonyManager.SIM_STATE_UNKNOWN:
- mPendingPinLock = false;
- break;
default:
if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
break;
@@ -1177,6 +1164,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
getRemoteSurfaceAlphaApplier().accept(0.0f);
mDreamingToLockscreenTransitionViewModel.get()
.startTransition();
+ mUnoccludeFromDreamFinishedCallback = finishedCallback;
return;
}
@@ -1256,6 +1244,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
};
}
+ private Consumer<TransitionStep> getFinishedCallbackConsumer() {
+ return (TransitionStep step) -> {
+ if (mUnoccludeFromDreamFinishedCallback == null) return;
+ try {
+ mUnoccludeFromDreamFinishedCallback.onAnimationFinished();
+ mUnoccludeFromDreamFinishedCallback = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Wasn't able to callback", e);
+ }
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
+ };
+ }
+
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -1524,6 +1525,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
collectFlow(viewRootImpl.getView(),
mDreamingToLockscreenTransitionViewModel.get().getDreamOverlayAlpha(),
getRemoteSurfaceAlphaApplier(), mMainDispatcher);
+ collectFlow(viewRootImpl.getView(),
+ mDreamingToLockscreenTransitionViewModel.get().getTransitionEnded(),
+ getFinishedCallbackConsumer(), mMainDispatcher);
}
}
// Most services aren't available until the system reaches the ready state, so we
@@ -2391,58 +2395,72 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
+ String message = "";
switch (msg.what) {
case SHOW:
+ message = "SHOW";
handleShow((Bundle) msg.obj);
break;
case HIDE:
+ message = "HIDE";
handleHide();
break;
case RESET:
+ message = "RESET";
handleReset(msg.arg1 != 0);
break;
case VERIFY_UNLOCK:
+ message = "VERIFY_UNLOCK";
Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
handleVerifyUnlock();
Trace.endSection();
break;
case NOTIFY_STARTED_GOING_TO_SLEEP:
+ message = "NOTIFY_STARTED_GOING_TO_SLEEP";
handleNotifyStartedGoingToSleep();
break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
+ message = "NOTIFY_FINISHED_GOING_TO_SLEEP";
handleNotifyFinishedGoingToSleep();
break;
case NOTIFY_STARTED_WAKING_UP:
+ message = "NOTIFY_STARTED_WAKING_UP";
Trace.beginSection(
"KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
handleNotifyStartedWakingUp();
Trace.endSection();
break;
case KEYGUARD_DONE:
+ message = "KEYGUARD_DONE";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
handleKeyguardDone();
Trace.endSection();
break;
case KEYGUARD_DONE_DRAWING:
+ message = "KEYGUARD_DONE_DRAWING";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_DRAWING");
handleKeyguardDoneDrawing();
Trace.endSection();
break;
case SET_OCCLUDED:
+ message = "SET_OCCLUDED";
Trace.beginSection("KeyguardViewMediator#handleMessage SET_OCCLUDED");
handleSetOccluded(msg.arg1 != 0, msg.arg2 != 0);
Trace.endSection();
break;
case KEYGUARD_TIMEOUT:
+ message = "KEYGUARD_TIMEOUT";
synchronized (KeyguardViewMediator.this) {
doKeyguardLocked((Bundle) msg.obj);
}
break;
case DISMISS:
- final DismissMessage message = (DismissMessage) msg.obj;
- handleDismiss(message.getCallback(), message.getMessage());
+ message = "DISMISS";
+ final DismissMessage dismissMsg = (DismissMessage) msg.obj;
+ handleDismiss(dismissMsg.getCallback(), dismissMsg.getMessage());
break;
case START_KEYGUARD_EXIT_ANIM:
+ message = "START_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
synchronized (KeyguardViewMediator.this) {
@@ -2460,21 +2478,25 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
Trace.endSection();
break;
case CANCEL_KEYGUARD_EXIT_ANIM:
+ message = "CANCEL_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage CANCEL_KEYGUARD_EXIT_ANIM");
handleCancelKeyguardExitAnimation();
Trace.endSection();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
+ message = "KEYGUARD_DONE_PENDING_TIMEOUT";
Trace.beginSection("KeyguardViewMediator#handleMessage"
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
case SYSTEM_READY:
+ message = "SYSTEM_READY";
handleSystemReady();
break;
}
+ Log.d(TAG, "KeyguardViewMediator queue processing message: " + message);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 128057ae6b62..3d8f6fd40e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -254,13 +254,17 @@ constructor(
private fun observeFaceDetectGatingChecks() {
// Face detection can run only when lockscreen bypass is enabled
- // & detection is supported & biometric unlock is not allowed.
+ // & detection is supported
+ // & biometric unlock is not allowed
+ // or user is trusted by trust manager & we want to run face detect to dismiss keyguard
listOf(
canFaceAuthOrDetectRun(faceDetectLog),
logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog),
logAndObserve(
- biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(),
- "nonStrongBiometricIsNotAllowed",
+ biometricSettingsRepository.isNonStrongBiometricAllowed
+ .isFalse()
+ .or(trustRepository.isCurrentUserTrusted),
+ "nonStrongBiometricIsNotAllowedOrCurrentUserIsTrusted",
faceDetectLog
),
// We don't want to run face detect if fingerprint can be used to unlock the device
@@ -312,18 +316,19 @@ constructor(
tableLogBuffer
),
logAndObserve(
- keyguardRepository.wakefulness.map { it.isStartingToSleepOrAsleep() }.isFalse(),
- "deviceNotSleepingOrNotStartingToSleep",
+ keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
+ "deviceNotStartingToSleep",
tableLogBuffer
),
logAndObserve(
- combine(
- keyguardInteractor.isSecureCameraActive,
- alternateBouncerInteractor.isVisible
- ) { a, b ->
- !a || b
- },
- "secureCameraNotActiveOrAltBouncerIsShowing",
+ keyguardInteractor.isSecureCameraActive
+ .isFalse()
+ .or(
+ alternateBouncerInteractor.isVisible.or(
+ keyguardInteractor.primaryBouncerShowing
+ )
+ ),
+ "secureCameraNotActiveOrAnyBouncerIsShowing",
tableLogBuffer
),
logAndObserve(
@@ -365,6 +370,7 @@ constructor(
"nonStrongBiometricIsAllowed",
faceAuthLog
),
+ logAndObserve(isAuthenticated.isFalse(), "faceNotAuthenticated", faceAuthLog),
)
.reduce(::and)
.distinctUntilChanged()
@@ -639,6 +645,10 @@ constructor(
private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
flow.combine(anotherFlow) { a, b -> a && b }
+/** Combine two boolean flows by or-ing both of them */
+private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>) =
+ this.combine(anotherFlow) { a, b -> a || b }
+
/** "Not" the given flow. The return [Flow] will be true when [this] flow is false. */
private fun Flow<Boolean>.isFalse(): Flow<Boolean> {
return this.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index b7963340228b..ed1bf3ef2753 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -167,9 +167,10 @@ constructor(
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.LOCKSCREEN,
animator =
- getDefaultAnimatorForTransitionsToState(KeyguardState.LOCKSCREEN).apply {
- duration = 0
- }
+ getDefaultAnimatorForTransitionsToState(
+ KeyguardState.LOCKSCREEN
+ )
+ .apply { duration = 0 }
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index a8147d0c2cf6..ff0db34ca06d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -156,7 +156,12 @@ constructor(
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
- interpolator = Interpolators.LINEAR
+ interpolator =
+ when (toState) {
+ KeyguardState.ALTERNATE_BOUNCER -> Interpolators.FAST_OUT_SLOW_IN
+ else -> Interpolators.LINEAR
+ }
+
duration =
when (toState) {
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 7c5641fcda9c..4f7abd4a2174 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.VERBOSE
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index df7c79ff4264..45bf20d3ec44 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -51,11 +52,35 @@ constructor(
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.GONE }
+ repository.transitions.filter { step -> step.to == GONE }
/** (any)->AOD transition information */
val anyStateToAodTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.AOD }
+ repository.transitions.filter { step -> step.to == AOD }
+
+ /** DREAMING->(any) transition information. */
+ val fromDreamingTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.from == DREAMING }
+
+ /** (any)->Lockscreen transition information */
+ val anyStateToLockscreenTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == LOCKSCREEN }
+
+ /** (any)->Occluded transition information */
+ val anyStateToOccludedTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == OCCLUDED }
+
+ /** (any)->PrimaryBouncer transition information */
+ val anyStateToPrimaryBouncerTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == PRIMARY_BOUNCER }
+
+ /** (any)->Dreaming transition information */
+ val anyStateToDreamingTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == DREAMING }
+
+ /** (any)->AlternateBouncer transition information */
+ val anyStateToAlternateBouncerTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == ALTERNATE_BOUNCER }
/** AOD->LOCKSCREEN transition information. */
val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
@@ -64,6 +89,9 @@ constructor(
val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN)
+ /** GONE->AOD transition information. */
+ val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
+
/** GONE->DREAMING transition information. */
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 8b749f0f4bc4..d467225a9d63 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -16,9 +16,12 @@
package com.android.systemui.keyguard.domain.interactor
+import android.content.Context
+import android.hardware.biometrics.BiometricFaceConstants
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
+import com.android.systemui.R
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -27,6 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise
@@ -34,7 +39,9 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -50,6 +57,7 @@ import kotlinx.coroutines.launch
class SystemUIKeyguardFaceAuthInteractor
@Inject
constructor(
+ private val context: Context,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val repository: DeviceEntryFaceAuthRepository,
@@ -157,17 +165,28 @@ constructor(
repository.cancel()
}
+ private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null)
/** Provide the status of face authentication */
- override val authenticationStatus = repository.authenticationStatus
+ override val authenticationStatus =
+ merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
/** Provide the status of face detection */
override val detectionStatus = repository.detectionStatus
private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
- applicationScope.launch {
- faceAuthenticationLogger.authRequested(uiEvent)
- repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
+ if (repository.isLockedOut.value) {
+ _authenticationStatusOverride.value =
+ ErrorAuthenticationStatus(
+ BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
+ context.resources.getString(R.string.keyguard_face_unlock_unavailable)
+ )
+ } else {
+ _authenticationStatusOverride.value = null
+ applicationScope.launch {
+ faceAuthenticationLogger.authRequested(uiEvent)
+ repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
+ }
}
} else {
faceAuthenticationLogger.ignoredFaceAuthTrigger(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index ae6fc9e6e6dc..0dda625a8b41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -44,9 +44,9 @@ sealed class TransitionInteractor(
abstract fun start()
fun startTransitionTo(
- toState: KeyguardState,
- animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
- resetIfCancelled: Boolean = false
+ toState: KeyguardState,
+ animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
+ resetIfCancelled: Boolean = false
): UUID? {
if (
fromState != transitionInteractor.startedKeyguardState.value &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
new file mode 100644
index 000000000000..bba0e37d8ed0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.FloatEvaluator
+import android.animation.IntEvaluator
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class UdfpsKeyguardInteractor
+@Inject
+constructor(
+ configRepo: ConfigurationRepository,
+ burnInInteractor: BurnInInteractor,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ private val intEvaluator = IntEvaluator()
+ private val floatEvaluator = FloatEvaluator()
+
+ val dozeAmount = keyguardInteractor.dozeAmount
+ val scaleForResolution = configRepo.scaleForResolution
+
+ /** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
+ val burnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ keyguardInteractor.dozeAmount,
+ burnInInteractor.udfpsBurnInXOffset,
+ burnInInteractor.udfpsBurnInYOffset,
+ burnInInteractor.udfpsBurnInProgress
+ ) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
+ BurnInOffsets(
+ intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
+ intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
+ floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
+ )
+ }
+}
+
+data class BurnInOffsets(
+ val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
+ val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
+ val burnInProgress: Float, // current progress based on the aodTransitionAmount
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index 9e7dec4dc1d0..b354cfd27687 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.shared.model
import android.hardware.face.FaceManager
+import android.os.SystemClock.elapsedRealtime
/**
* Authentication status provided by
@@ -38,8 +39,12 @@ data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationS
object FailedAuthenticationStatus : AuthenticationStatus()
/** Face authentication error message */
-data class ErrorAuthenticationStatus(val msgId: Int, val msg: String? = null) :
- AuthenticationStatus() {
+data class ErrorAuthenticationStatus(
+ val msgId: Int,
+ val msg: String? = null,
+ // present to break equality check if the same error occurs repeatedly.
+ val createdAt: Long = elapsedRealtime()
+) : AuthenticationStatus() {
/**
* Method that checks if [msgId] is a lockout error. A lockout error means that face
* authentication is locked out.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
new file mode 100644
index 000000000000..ebf1beb132f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.keyguard.ui.adapter
+
+/**
+ * Temporary adapter class while
+ * [com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController] is being refactored
+ * before [com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy] is removed.
+ *
+ * TODO (b/278719514): Delete once udfps keyguard view is fully refactored.
+ */
+interface UdfpsKeyguardViewControllerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
new file mode 100644
index 000000000000..728dd3911663
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsAodFingerprintViewBinder {
+
+ /**
+ * Drives UI for the UDFPS aod fingerprint view. See [UdfpsFingerprintViewBinder] and
+ * [UdfpsBackgroundViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: LottieAnimationView,
+ viewModel: UdfpsAodViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.progress = burnInOffsets.burnInProgress
+ view.translationX = burnInOffsets.burnInXOffset.toFloat()
+ view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ }
+ }
+
+ launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
+
+ launch {
+ viewModel.padding.collect { padding ->
+ view.setPadding(padding, padding, padding, padding)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
new file mode 100644
index 000000000000..26ef4685d286
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.content.res.ColorStateList
+import android.widget.ImageView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsBackgroundViewBinder {
+
+ /**
+ * Drives UI for the udfps background view. See [UdfpsAodFingerprintViewBinder] and
+ * [UdfpsFingerprintViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: ImageView,
+ viewModel: BackgroundViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.transition.collect {
+ view.alpha = it.alpha
+ view.scaleX = it.scale
+ view.scaleY = it.scale
+ view.imageTintList = ColorStateList.valueOf(it.color)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
new file mode 100644
index 000000000000..0ab8e52fb6c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.model.KeyPath
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsFingerprintViewBinder {
+ private var udfpsIconColor = 0
+
+ /**
+ * Drives UI for the UDFPS fingerprint view when it's NOT on aod. See
+ * [UdfpsAodFingerprintViewBinder] and [UdfpsBackgroundViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: LottieAnimationView,
+ viewModel: FingerprintViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.transition.collect {
+ view.alpha = it.alpha
+ view.scaleX = it.scale
+ view.scaleY = it.scale
+ if (udfpsIconColor != (it.color)) {
+ udfpsIconColor = it.color
+ view.invalidate()
+ }
+ }
+ }
+
+ launch {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.burnInXOffset.toFloat()
+ view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ }
+ }
+
+ launch {
+ viewModel.dozeAmount.collect { dozeAmount ->
+ // Lottie progress represents: aod=0 to lockscreen=1
+ view.progress = 1f - dozeAmount
+ }
+ }
+
+ launch {
+ viewModel.padding.collect { padding ->
+ view.setPadding(padding, padding, padding, padding)
+ }
+ }
+ }
+ }
+
+ // Add a callback that updates the color to `udfpsIconColor` whenever invalidate is called
+ view.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(udfpsIconColor, PorterDuff.Mode.SRC_ATOP)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
new file mode 100644
index 000000000000..b568a9af9bb8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.binder.UdfpsAodFingerprintViewBinder
+import com.android.systemui.keyguard.ui.binder.UdfpsBackgroundViewBinder
+import com.android.systemui.keyguard.ui.binder.UdfpsFingerprintViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+object UdfpsKeyguardInternalViewBinder {
+
+ @JvmStatic
+ fun bind(
+ view: View,
+ viewModel: UdfpsKeyguardInternalViewModel,
+ aodViewModel: UdfpsAodViewModel,
+ fingerprintViewModel: FingerprintViewModel,
+ backgroundViewModel: BackgroundViewModel,
+ ) {
+ view.accessibilityDelegate = viewModel.accessibilityDelegate
+
+ // bind child views
+ UdfpsAodFingerprintViewBinder.bind(view.findViewById(R.id.udfps_aod_fp), aodViewModel)
+ UdfpsFingerprintViewBinder.bind(
+ view.findViewById(R.id.udfps_lockscreen_fp),
+ fingerprintViewModel
+ )
+ UdfpsBackgroundViewBinder.bind(
+ view.findViewById(R.id.udfps_keyguard_fp_bg),
+ backgroundViewModel
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
new file mode 100644
index 000000000000..667abaea0b24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.graphics.RectF
+import android.view.View
+import android.widget.FrameLayout
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsKeyguardViewBinder {
+ /**
+ * Drives UI for the keyguard UDFPS view. Inflates child views on a background thread. For view
+ * binders for its child views, see [UdfpsFingerprintViewBinder], [UdfpsBackgroundViewBinder] &
+ * [UdfpsAodFingerprintViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: UdfpsKeyguardView,
+ viewModel: UdfpsKeyguardViewModel,
+ udfpsKeyguardInternalViewModel: UdfpsKeyguardInternalViewModel,
+ aodViewModel: UdfpsAodViewModel,
+ fingerprintViewModel: FingerprintViewModel,
+ backgroundViewModel: BackgroundViewModel,
+ ) {
+ view.useExpandedOverlay(viewModel.useExpandedOverlay())
+
+ val layoutInflaterFinishListener =
+ AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
+ UdfpsKeyguardInternalViewBinder.bind(
+ inflatedInternalView,
+ udfpsKeyguardInternalViewModel,
+ aodViewModel,
+ fingerprintViewModel,
+ backgroundViewModel,
+ )
+ if (viewModel.useExpandedOverlay()) {
+ val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
+ lp.width = viewModel.sensorBounds.width()
+ lp.height = viewModel.sensorBounds.height()
+ val relativeToView =
+ getBoundsRelativeToView(
+ inflatedInternalView,
+ RectF(viewModel.sensorBounds),
+ )
+ lp.setMarginsRelative(
+ relativeToView.left.toInt(),
+ relativeToView.top.toInt(),
+ relativeToView.right.toInt(),
+ relativeToView.bottom.toInt(),
+ )
+ parent!!.addView(inflatedInternalView, lp)
+ } else {
+ parent!!.addView(inflatedInternalView)
+ }
+ }
+ val inflater = AsyncLayoutInflater(view.context)
+ inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
+
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ combine(aodViewModel.isVisible, fingerprintViewModel.visible) {
+ isAodVisible,
+ isFingerprintVisible ->
+ isAodVisible || isFingerprintVisible
+ }
+ .collect { view.setVisible(it) }
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts coordinates of RectF relative to the screen to coordinates relative to this view.
+ *
+ * @param bounds RectF based off screen coordinates in current orientation
+ */
+ private fun getBoundsRelativeToView(view: View, bounds: RectF): RectF {
+ val pos: IntArray = view.locationOnScreen
+ return RectF(
+ bounds.left - pos[0],
+ bounds.top - pos[1],
+ bounds.right - pos[0],
+ bounds.bottom - pos[1]
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a62f383ed704..0077f2d68c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,10 +19,7 @@ package com.android.systemui.keyguard.ui.view
import android.content.Context
import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import com.android.keyguard.LockIconView
import com.android.systemui.R
@@ -31,7 +28,7 @@ class KeyguardRootView(
context: Context,
private val attrs: AttributeSet?,
) :
- FrameLayout(
+ ConstraintLayout(
context,
attrs,
) {
@@ -43,31 +40,11 @@ class KeyguardRootView(
private fun addIndicationTextArea() {
val view = KeyguardIndicationArea(context, attrs)
- addView(
- view,
- FrameLayout.LayoutParams(
- MATCH_PARENT,
- WRAP_CONTENT,
- )
- .apply {
- gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
- bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp()
- }
- )
+ addView(view)
}
private fun addLockIconView() {
val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
- addView(
- view,
- LayoutParams(
- WRAP_CONTENT,
- WRAP_CONTENT,
- )
- )
- }
-
- private fun Int.dp(): Int {
- return context.resources.getDimensionPixelSize(this)
+ addView(view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
new file mode 100644
index 000000000000..baaeb60f364e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import javax.inject.Inject
+
+/**
+ * Positions elements of the lockscreen to the default position.
+ *
+ * This will be the most common use case for phones in portrait mode.
+ */
+@SysUISingleton
+class DefaultLockscreenLayout
+@Inject
+constructor(
+ private val authController: AuthController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val windowManager: WindowManager,
+ private val context: Context,
+) : LockscreenLayout {
+ override val id: String = DEFAULT
+
+ override fun layoutIndicationArea(rootView: KeyguardRootView) {
+ val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(indicationArea.id, MATCH_PARENT)
+ constrainHeight(indicationArea.id, WRAP_CONTENT)
+ connect(
+ indicationArea.id,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ R.dimen.keyguard_indication_margin_bottom.dp()
+ )
+ connect(indicationArea.id, START, PARENT_ID, START)
+ connect(indicationArea.id, END, PARENT_ID, END)
+ applyTo(rootView)
+ }
+ }
+
+ override fun layoutLockIcon(rootView: KeyguardRootView) {
+ val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
+ val scaleFactor: Float = authController.scaleFactor
+ val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp()
+ val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp()
+ val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val widthPixels = bounds.right.toFloat()
+ val heightPixels = bounds.bottom.toFloat()
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+ if (isUdfpsSupported) {
+ authController.udfpsLocation?.let { udfpsLocation ->
+ centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView)
+ }
+ } else {
+ centerLockIcon(
+ Point(
+ (widthPixels / 2).toInt(),
+ (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+ ),
+ lockIconRadiusPx * scaleFactor,
+ scaledPadding,
+ rootView
+ )
+ }
+ }
+
+ @VisibleForTesting
+ internal fun centerLockIcon(
+ center: Point,
+ radius: Float,
+ drawablePadding: Int,
+ rootView: KeyguardRootView,
+ ) {
+ val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
+ val lockIcon = lockIconView.findViewById<View>(R.id.lock_icon) ?: return
+ lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding)
+
+ val sensorRect =
+ Rect().apply {
+ set(
+ center.x - radius.toInt(),
+ center.y - radius.toInt(),
+ center.x + radius.toInt(),
+ center.y + radius.toInt(),
+ )
+ }
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left)
+ constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top)
+ connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top)
+ connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left)
+ applyTo(rootView)
+ }
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+
+ private fun ConstraintLayout.getConstraintSet(): ConstraintSet {
+ val cs = ConstraintSet()
+ cs.clone(this)
+ return cs
+ }
+
+ companion object {
+ const val DEFAULT = "default"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
new file mode 100644
index 000000000000..9bc630265a3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.res.Configuration
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/**
+ * Manages layout changes for the lockscreen.
+ *
+ * To add a layout, add an entry to the map with a unique id and call #transitionToLayout(string).
+ */
+@SysUISingleton
+class KeyguardLayoutManager
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ layouts: Set<@JvmSuppressWildcards LockscreenLayout>,
+ private val keyguardRootView: KeyguardRootView,
+) {
+ internal val layoutIdMap: Map<String, LockscreenLayout> = layouts.associateBy { it.id }
+ private var layout: LockscreenLayout? = layoutIdMap[DEFAULT]
+
+ init {
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ layoutViews()
+ }
+ }
+ )
+ }
+
+ /**
+ * Transitions to a layout.
+ *
+ * @param layoutId
+ * @return whether the transition has succeeded.
+ */
+ fun transitionToLayout(layoutId: String): Boolean {
+ layout = layoutIdMap[layoutId] ?: return false
+ layoutViews()
+ return true
+ }
+
+ fun layoutViews() {
+ layout?.layoutViews(keyguardRootView)
+ }
+
+ companion object {
+ const val TAG = "KeyguardLayoutManager"
+ }
+}
+
+interface LockscreenLayout {
+ val id: String
+
+ fun layoutViews(rootView: KeyguardRootView) {
+ // Clear constraints.
+ ConstraintSet()
+ .apply {
+ clone(rootView)
+ knownIds.forEach { getConstraint(it).layout.copyFrom(ConstraintSet.Layout()) }
+ }
+ .applyTo(rootView)
+ layoutIndicationArea(rootView)
+ layoutLockIcon(rootView)
+ }
+ fun layoutIndicationArea(rootView: KeyguardRootView)
+ fun layoutLockIcon(rootView: KeyguardRootView)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
new file mode 100644
index 000000000000..b351ea8923f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Uses $ adb shell cmd statusbar layout <LayoutId> */
+class KeyguardLayoutManagerCommandListener
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val keyguardLayoutManager: KeyguardLayoutManager
+) {
+ private val layoutCommand = KeyguardLayoutManagerCommand()
+
+ fun start() {
+ commandRegistry.registerCommand(COMMAND) { layoutCommand }
+ }
+
+ internal inner class KeyguardLayoutManagerCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ if (keyguardLayoutManager.transitionToLayout(arg)) {
+ pw.println("Transition succeeded!")
+ } else {
+ pw.println("Invalid argument! To see available layout ids, run:")
+ pw.println("$ adb shell cmd statusbar layout help")
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar layout <layoutId>")
+ pw.println("Existing Layout Ids: ")
+ keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") }
+ }
+ }
+
+ companion object {
+ internal const val COMMAND = "layout"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
new file mode 100644
index 000000000000..00f93e33f370
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+abstract class LockscreenLayoutModule {
+ @Binds
+ @IntoSet
+ abstract fun bindDefaultLayout(
+ defaultLockscreenLayout: DefaultLockscreenLayout
+ ): LockscreenLayout
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 9ca4bd62b6fe..e24d326850e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -48,7 +48,7 @@ constructor(
)
val transitionEnded =
- keyguardTransitionInteractor.dreamingToLockscreenTransition.filter { step ->
+ keyguardTransitionInteractor.fromDreamingTransition.filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
new file mode 100644
index 000000000000..667c2f1bd998
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** View-model for UDFPS AOD view. */
+@ExperimentalCoroutinesApi
+class UdfpsAodViewModel
+@Inject
+constructor(
+ val interactor: UdfpsKeyguardInteractor,
+ val context: Context,
+) {
+ val alpha: Flow<Float> = interactor.dozeAmount
+ val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val isVisible: Flow<Boolean> = alpha.map { it != 0f }
+
+ // Padding between the fingerprint icon and its bounding box in pixels.
+ val padding: Flow<Int> =
+ interactor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
new file mode 100644
index 000000000000..d894a1139eeb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.biometrics.UdfpsKeyguardAccessibilityDelegate
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardInternalViewModel
+@Inject
+constructor(val accessibilityDelegate: UdfpsKeyguardAccessibilityDelegate)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
new file mode 100644
index 000000000000..929f27f4fea3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Rect
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardViewModel
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+) {
+ var sensorBounds: Rect = Rect()
+
+ fun useExpandedOverlay(): Boolean {
+ return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
new file mode 100644
index 000000000000..098b481491de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Rect
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.binder.UdfpsKeyguardViewBinder
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class UdfpsKeyguardViewModels
+@Inject
+constructor(
+ private val viewModel: UdfpsKeyguardViewModel,
+ private val internalViewModel: UdfpsKeyguardInternalViewModel,
+ private val aodViewModel: UdfpsAodViewModel,
+ private val lockscreenFingerprintViewModel: FingerprintViewModel,
+ private val lockscreenBackgroundViewModel: BackgroundViewModel,
+) {
+
+ fun setSensorBounds(sensorBounds: Rect) {
+ viewModel.sensorBounds = sensorBounds
+ }
+
+ fun bindViews(view: UdfpsKeyguardView) {
+ UdfpsKeyguardViewBinder.bind(
+ view,
+ viewModel,
+ internalViewModel,
+ aodViewModel,
+ lockscreenFingerprintViewModel,
+ lockscreenBackgroundViewModel
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
new file mode 100644
index 000000000000..fd4b666a80fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import androidx.annotation.ColorInt
+import com.android.settingslib.Utils.getColorAttrDefaultColor
+import com.android.systemui.R
+import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** View-model for UDFPS lockscreen views. */
+@ExperimentalCoroutinesApi
+open class UdfpsLockscreenViewModel(
+ context: Context,
+ lockscreenColorResId: Int,
+ alternateBouncerColorResId: Int,
+ transitionInteractor: KeyguardTransitionInteractor,
+) {
+ private val toLockscreen: Flow<TransitionViewModel> =
+ transitionInteractor.anyStateToLockscreenTransition.map {
+ TransitionViewModel(
+ alpha =
+ if (it.from == KeyguardState.AOD) {
+ it.value // animate
+ } else {
+ 1f
+ },
+ scale = 1f,
+ color = getColorAttrDefaultColor(context, lockscreenColorResId),
+ )
+ }
+
+ private val toAlternateBouncer: Flow<TransitionViewModel> =
+ transitionInteractor.anyStateToAlternateBouncerTransition.map {
+ TransitionViewModel(
+ alpha = 1f,
+ scale =
+ if (visibleInKeyguardState(it.from)) {
+ 1f
+ } else {
+ it.value
+ },
+ color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
+ )
+ }
+
+ private val fadeOut: Flow<TransitionViewModel> =
+ merge(
+ transitionInteractor.anyStateToGoneTransition,
+ transitionInteractor.anyStateToAodTransition,
+ transitionInteractor.anyStateToOccludedTransition,
+ transitionInteractor.anyStateToPrimaryBouncerTransition,
+ transitionInteractor.anyStateToDreamingTransition,
+ )
+ .map {
+ TransitionViewModel(
+ alpha =
+ if (visibleInKeyguardState(it.from)) {
+ 1f - it.value
+ } else {
+ 0f
+ },
+ scale = 1f,
+ color =
+ if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
+ getColorAttrDefaultColor(context, alternateBouncerColorResId)
+ } else {
+ getColorAttrDefaultColor(context, lockscreenColorResId)
+ },
+ )
+ }
+
+ private fun visibleInKeyguardState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF,
+ KeyguardState.DOZING,
+ KeyguardState.DREAMING,
+ KeyguardState.AOD,
+ KeyguardState.PRIMARY_BOUNCER,
+ KeyguardState.GONE,
+ KeyguardState.OCCLUDED -> false
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER -> true
+ }
+ }
+
+ val transition: Flow<TransitionViewModel> =
+ merge(
+ toAlternateBouncer,
+ toLockscreen,
+ fadeOut,
+ )
+ val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
+}
+
+@ExperimentalCoroutinesApi
+class FingerprintViewModel
+@Inject
+constructor(
+ val context: Context,
+ transitionInteractor: KeyguardTransitionInteractor,
+ interactor: UdfpsKeyguardInteractor,
+) :
+ UdfpsLockscreenViewModel(
+ context,
+ android.R.attr.textColorPrimary,
+ com.android.internal.R.attr.materialColorOnPrimaryFixed,
+ transitionInteractor,
+ ) {
+ val dozeAmount: Flow<Float> = interactor.dozeAmount
+ val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+
+ // Padding between the fingerprint icon and its bounding box in pixels.
+ val padding: Flow<Int> =
+ interactor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+}
+
+@ExperimentalCoroutinesApi
+class BackgroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ transitionInteractor: KeyguardTransitionInteractor,
+) :
+ UdfpsLockscreenViewModel(
+ context,
+ com.android.internal.R.attr.colorSurface,
+ com.android.internal.R.attr.materialColorPrimaryFixed,
+ transitionInteractor,
+ )
+
+data class TransitionViewModel(
+ val alpha: Float,
+ val scale: Float,
+ @ColorInt val color: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
index 3226865d1d82..d4b799f444e5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
@@ -18,6 +18,7 @@ package com.android.systemui.log
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.BouncerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index fefa1b29b576..68cdfb6d5865 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -6,7 +6,7 @@ import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
index 27301e92eca2..150de26c12c7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
@@ -21,9 +21,9 @@ import android.graphics.Rect
import android.graphics.RectF
import androidx.core.graphics.toRectF
import com.android.systemui.dagger.SysUISingleton
-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.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.ScreenDecorationsLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 8d622ae1ca03..67a985eb44bc 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -21,8 +21,8 @@ import android.os.Trace
import com.android.systemui.Dumpable
import com.android.systemui.common.buffer.RingBuffer
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.log.TableLogBufferBase
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
index e2e269de71a0..534241edb253 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.media.controls.pipeline
import android.media.session.PlaybackState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaTimeoutListenerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
index 9e53d77dec99..888b9c7cc901 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.media.controls.resume
import android.content.ComponentName
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaBrowserLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 30ee147e302a..2883210805d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -128,6 +128,15 @@ constructor(
var visibilityChangedListener: ((Boolean) -> Unit)? = null
+ /**
+ * Whether the doze wake up animation is delayed and we are currently waiting for it to start.
+ */
+ var isDozeWakeUpAnimationWaiting: Boolean = false
+ set(value) {
+ field = value
+ refreshMediaPosition()
+ }
+
/** single pane media container placed at the top of the notifications list */
var singlePaneContainer: MediaContainerView? = null
private set
@@ -221,7 +230,13 @@ constructor(
// by the clock. This is not the case for single-line clock though.
// For single shade, we don't need to do it, because media is a child of NSSL, which already
// gets hidden on AOD.
- return !statusBarStateController.isDozing
+ // Media also has to be hidden when waking up from dozing, and the doze wake up animation is
+ // delayed and waiting to be started.
+ // This is to stay in sync with the delaying of the horizontal alignment of the rest of the
+ // keyguard container, that is also delayed until the "wait" is over.
+ // If we show media during this waiting period, the shade will still be centered, and using
+ // the entire width of the screen, and making media show fully stretched.
+ return !statusBarStateController.isDozing && !isDozeWakeUpAnimationWaiting
}
private fun showMediaPlayer() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
index 0ed24349bdf4..3dc00004e900 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.media.controls.ui
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaCarouselControllerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
index c781b7699b26..8f1595d7d7a2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.media.controls.ui
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaViewLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 318cd99a06ed..26a7d048cf27 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -20,6 +20,7 @@ import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECT
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -181,27 +182,23 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.getSelectedMediaDevice(), device)));
boolean isHost = device.isHostForOngoingSession()
&& isActiveWithOngoingSession;
- if (isHost) {
+ if (isActiveWithOngoingSession) {
mCurrentActivePosition = position;
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
mSubTitleText.setText(device.getSubtextString());
updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA);
- updateEndClickAreaAsSessionEditing(device);
+ updateEndClickAreaAsSessionEditing(device,
+ isHost ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
setTwoLineLayout(device, null /* title */, true /* bFocused */,
true /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */, false /* showStatus */,
true /* showEndTouchArea */, false /* isFakeActive */);
initSeekbar(device, isCurrentSeekbarInvisible);
} else {
- if (isActiveWithOngoingSession) {
- //Selected device which has ongoing session, disable seekbar since we
- //only allow volume control on Host
+ if (currentlyConnected) {
mCurrentActivePosition = position;
- }
- boolean showSeekbar =
- (!device.hasOngoingSession() && currentlyConnected);
- if (showSeekbar) {
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -222,10 +219,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
setTwoLineLayout(device, currentlyConnected /* bFocused */,
- showSeekbar /* showSeekBar */,
+ currentlyConnected /* showSeekBar */,
false /* showProgressBar */, true /* showSubtitle */,
deviceStatusIcon != null /* showStatus */,
- isActiveWithOngoingSession /* isFakeActive */);
+ false /* isFakeActive */);
}
} else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setUpDeviceIcon(device);
@@ -267,25 +264,16 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
setSingleLineLayout(getItemTitle(device));
} else if (device.hasOngoingSession()) {
mCurrentActivePosition = position;
- if (device.isHostForOngoingSession()) {
- updateTitleIcon(R.drawable.media_output_icon_volume,
- mController.getColorItemContent());
- updateEndClickAreaAsSessionEditing(device);
- mEndClickIcon.setVisibility(View.VISIBLE);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
- false /* showProgressBar */, false /* showCheckBox */,
- true /* showEndTouchArea */);
- initSeekbar(device, isCurrentSeekbarInvisible);
- } else {
- updateDeviceStatusIcon(mContext.getDrawable(
- R.drawable.ic_sound_bars_anim));
- mStatusIcon.setVisibility(View.VISIBLE);
- updateSingleLineLayoutContentAlpha(
- updateClickActionBasedOnSelectionBehavior(device)
- ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
- setSingleLineLayout(getItemTitle(device));
- initFakeActiveDevice();
- }
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateEndClickAreaAsSessionEditing(device, device.isHostForOngoingSession()
+ ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
+ mEndClickIcon.setVisibility(View.VISIBLE);
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ true /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
} else if (mController.isCurrentConnectedDeviceRemote()
&& !mController.getSelectableMediaDevice().isEmpty()) {
//If device is connected and there's other selectable devices, layout as
@@ -362,7 +350,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mStatusIcon.setAlpha(alphaValue);
}
- private void updateEndClickAreaAsSessionEditing(MediaDevice device) {
+ private void updateEndClickAreaAsSessionEditing(MediaDevice device, @DrawableRes int id) {
mEndClickIcon.setOnClickListener(null);
mEndTouchArea.setOnClickListener(null);
updateEndClickAreaColor(mController.getColorSeekbarProgress());
@@ -371,6 +359,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
+ Drawable drawable = mContext.getDrawable(id);
+ mEndClickIcon.setImageDrawable(drawable);
+ if (drawable instanceof AnimatedVectorDrawable) {
+ ((AnimatedVectorDrawable) drawable).start();
+ }
}
public void updateEndClickAreaColor(int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
index bbcf259418c8..417168209b43 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
@@ -3,7 +3,7 @@ package com.android.systemui.media.muteawait
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.MediaMuteAwaitLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [MediaMuteAwaitConnectionManager]. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
index 66399d580582..46c0132deff7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
@@ -3,7 +3,7 @@ package com.android.systemui.media.nearby
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.NearbyMediaDevicesLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [NearbyMediaDevicesManager]. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
index eeda102702d2..3c2226f6a240 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -17,7 +17,7 @@
package com.android.systemui.media.taptotransfer.common
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** A helper for logging media tap-to-transfer events. */
object MediaTttLoggerUtils {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 206e5e3ee090..503afd3a675a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -20,7 +20,7 @@ import android.app.StatusBarManager
import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 8d809908d78b..07846b56d784 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -114,7 +114,7 @@ public class SysUiState implements Dumpable {
pw.print(" mSysUiStateFlags="); pw.println(mFlags);
pw.println(" " + QuickStepContract.getSystemUiStateString(mFlags));
pw.print(" backGestureDisabled=");
- pw.println(QuickStepContract.isBackGestureDisabled(mFlags));
+ pw.println(QuickStepContract.isBackGestureDisabled(mFlags, false /* forTrackpad */));
pw.print(" assistantGestureDisabled=");
pw.println(QuickStepContract.isAssistantGestureDisabled(mFlags));
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 99c591f25edb..8225c47d904b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -462,7 +462,7 @@ public final class NavBarHelper implements
* @return Whether the IME is shown on top of the screen given the {@code vis} flag of
* {@link InputMethodService} and the keyguard states.
*/
- public boolean isImeShown(@InputMethodService.ImeWindowVisibility int vis) {
+ public boolean isImeShown(int vis) {
View shadeWindowView = mNotificationShadeWindowController.getWindowRootView();
boolean isKeyguardShowing = mKeyguardStateController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5bae1cba4ac4..682335e0b419 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -66,7 +66,6 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.inputmethodservice.InputMethodService;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -1048,9 +1047,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
// ----- CommandQueue Callbacks -----
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index cecf043c572e..3b32313e76a0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -338,9 +338,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
boolean imeShown = mNavBarHelper.isImeShown(vis);
if (!imeShown) {
// Count imperceptible changes as visible so we transition taskbar out quickly.
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 7b86d0a6ebce..a8af67a9fc97 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -1023,7 +1023,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
boolean isBackAllowedCommon = !mDisabledForQuickstep && mIsBackGestureAllowed
&& !mGestureBlockingActivityRunning
- && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
+ && !QuickStepContract.isBackGestureDisabled(mSysUiFlags,
+ mIsTrackpadThreeFingerSwipe)
&& !isTrackpadScroll(mIsTrackpadGestureFeaturesEnabled, ev);
if (mIsTrackpadThreeFingerSwipe) {
// Trackpad back gestures don't have zones, so we don't need to check if the down
@@ -1056,8 +1057,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
+ " disp=%s, wl=%d, il=%d, wr=%d, ir=%d, excl=%s]",
curTime, curTimeStr, mAllowGesture, mIsTrackpadThreeFingerSwipe,
mIsOnLeftEdge, mDeferSetIsOnLeftEdge, mIsBackGestureAllowed,
- QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisabledForQuickstep,
- mGestureBlockingActivityRunning, mIsInPip, mDisplaySize,
+ QuickStepContract.isBackGestureDisabled(mSysUiFlags,
+ mIsTrackpadThreeFingerSwipe),
+ mDisabledForQuickstep, mGestureBlockingActivityRunning, mIsInPip, mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index e56106d1c065..f934346d9775 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -20,8 +20,8 @@ import android.icu.text.SimpleDateFormat
import android.permission.PermissionGroupUsage
import com.android.systemui.log.dagger.PrivacyLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
import com.android.systemui.privacy.PrivacyDialog
import com.android.systemui.privacy.PrivacyItem
import java.util.Locale
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
index ac6aabb2e5bd..6563e425190d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
@@ -2,7 +2,7 @@ package com.android.systemui.qs
import com.android.systemui.log.dagger.QSFragmentDisableLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 6265b3c056e7..3432628e6d67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -18,6 +18,7 @@ package com.android.systemui.qs.external
import android.content.Context
import android.graphics.drawable.Icon
+import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
@@ -66,7 +67,8 @@ class TileRequestDialog(
}
private fun createTileView(tileData: TileData): QSTileView {
- val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
+ val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ val tile = QSTileViewImpl(themedContext, QSIconViewImpl(themedContext), true)
val state = QSTile.BooleanState().apply {
label = tileData.label
handlesLongClick = false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 9c9ad33e4918..3c53d77c6beb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -244,8 +244,8 @@ class FooterActionsViewBinder @Inject constructor() {
val backgroundResource =
when (model.backgroundColor) {
- R.attr.offStateColor -> R.drawable.qs_footer_action_circle
- com.android.internal.R.attr.colorAccent -> R.drawable.qs_footer_action_circle_color
+ R.attr.shadeInactive -> R.drawable.qs_footer_action_circle
+ R.attr.shadeActive -> R.drawable.qs_footer_action_circle_color
else -> error("Unsupported icon background resource ${model.backgroundColor}")
}
buttonView.setBackgroundResource(backgroundResource)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index b3596a254b7d..32146b5b00e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -145,8 +145,12 @@ class FooterActionsViewModel(
R.drawable.ic_settings,
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
),
- iconTint = null,
- backgroundColor = R.attr.offStateColor,
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ context,
+ R.attr.onShadeInactiveVariant,
+ ),
+ backgroundColor = R.attr.shadeInactive,
this::onSettingsButtonClicked,
)
@@ -162,9 +166,9 @@ class FooterActionsViewModel(
iconTint =
Utils.getColorAttrDefaultColor(
context,
- com.android.internal.R.attr.textColorOnAccent,
+ R.attr.onShadeActive,
),
- backgroundColor = com.android.internal.R.attr.colorAccent,
+ backgroundColor = R.attr.shadeActive,
this::onPowerButtonClicked,
)
} else {
@@ -264,7 +268,7 @@ class FooterActionsViewModel(
),
),
iconTint = null,
- backgroundColor = R.attr.offStateColor,
+ backgroundColor = R.attr.shadeInactive,
onClick = this::onUserSwitcherClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index c00a81cbf12b..39745c8cbeea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -24,9 +24,9 @@ import android.view.View
import com.android.systemui.log.ConstantStringsLogger
import com.android.systemui.log.ConstantStringsLoggerImpl
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.VERBOSE
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.log.dagger.QSConfigLog
import com.android.systemui.log.dagger.QSLog
import com.android.systemui.plugins.qs.QSTile
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index 498f403e8c7a..6e7e09959697 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -51,12 +51,26 @@ class InstalledTilesComponentRepositoryImpl
@Inject
constructor(
@Application private val applicationContext: Context,
- private val packageManager: PackageManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : InstalledTilesComponentRepository {
- override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> =
- conflatedCallbackFlow {
+ override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> {
+ /*
+ * In order to query [PackageManager] for different users, this implementation will call
+ * [Context.createContextAsUser] and retrieve the [PackageManager] from that context.
+ */
+ val packageManager =
+ if (applicationContext.userId == userId) {
+ applicationContext.packageManager
+ } else {
+ applicationContext
+ .createContextAsUser(
+ UserHandle.of(userId),
+ /* flags */ 0,
+ )
+ .packageManager
+ }
+ return conflatedCallbackFlow {
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@@ -74,12 +88,13 @@ constructor(
awaitClose { applicationContext.unregisterReceiver(receiver) }
}
.onStart { emit(Unit) }
- .map { reloadComponents(userId) }
+ .map { reloadComponents(userId, packageManager) }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ }
@WorkerThread
- private fun reloadComponents(userId: Int): Set<ComponentName> {
+ private fun reloadComponents(userId: Int, packageManager: PackageManager): Set<ComponentName> {
return packageManager
.queryIntentServicesAsUser(INTENT, FLAGS, userId)
.mapNotNull { it.serviceInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index d400faa3091e..573cb7154c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.pipeline.shared.logging
import android.annotation.UserIdInt
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
import com.android.systemui.qs.pipeline.dagger.QSTileListLog
import com.android.systemui.qs.pipeline.shared.TileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index e54168162de6..7e45491adc83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -248,13 +248,11 @@ public class QSIconViewImpl extends QSIconView {
*/
private static int getIconColorForState(Context context, QSTile.State state) {
if (state.disabledByPolicy || state.state == Tile.STATE_UNAVAILABLE) {
- return Utils.getColorAttrDefaultColor(
- context, com.android.internal.R.attr.textColorTertiary);
+ return Utils.getColorAttrDefaultColor(context, R.attr.outline);
} else if (state.state == Tile.STATE_INACTIVE) {
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
+ return Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant);
} else if (state.state == Tile.STATE_ACTIVE) {
- return Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.textColorOnAccent);
+ return Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive);
} else {
Log.e("QSIconView", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 2a9e7d05c187..1ca2a961744b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -330,7 +330,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
final int eventId = mClickEventId++;
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
eventId);
- mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
+ if (!mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+ mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
+ }
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index b80668379e49..d81e4c229aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -29,6 +29,7 @@ import android.os.Trace
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
+import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@@ -92,24 +93,21 @@ open class QSTileViewImpl @JvmOverloads constructor(
updateHeight()
}
- private val colorActive = Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.colorAccentPrimary)
- private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor)
- private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive)
+ private val colorActive = Utils.getColorAttrDefaultColor(context, R.attr.shadeActive)
+ private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
+ private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
- private val colorLabelActive =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent)
- private val colorLabelInactive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
+ private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
private val colorLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
+ Utils.getColorAttrDefaultColor(context, R.attr.outline)
private val colorSecondaryLabelActive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse)
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant)
private val colorSecondaryLabelInactive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary)
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
private val colorSecondaryLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
+ Utils.getColorAttrDefaultColor(context, R.attr.outline)
private lateinit var label: TextView
protected lateinit var secondaryLabel: TextView
@@ -151,6 +149,11 @@ open class QSTileViewImpl @JvmOverloads constructor(
private val locInScreen = IntArray(2)
init {
+ val typedValue = TypedValue()
+ if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) {
+ throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " +
+ "Theme.SystemUI.QuickSettings")
+ }
setId(generateViewId())
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL or Gravity.START
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2ef9e0772d93..d97db3b27c87 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -117,6 +117,7 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -208,7 +209,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -238,7 +238,7 @@ import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public final class NotificationPanelViewController implements ShadeSurface, Dumpable {
public static final String TAG = NotificationPanelView.class.getSimpleName();
@@ -1175,6 +1175,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
updateClockAppearance();
if (mKeyguardUserSwitcherController != null) {
@@ -1227,6 +1228,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void onSplitShadeEnabledChanged() {
mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
@@ -1407,11 +1409,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardBottomArea = keyguardBottomArea;
}
- void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ @Override
+ public void setOpenCloseListener(OpenCloseListener openCloseListener) {
mOpenCloseListener = openCloseListener;
}
- void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+ @Override
+ public void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
mTrackingStartedListener = trackingStartedListener;
}
@@ -1623,6 +1627,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mWillPlayDelayedDozeAmountAnimation = willPlay;
mWakeUpCoordinator.logDelayingClockWakeUpAnimation(willPlay);
+ mKeyguardMediaController.setDozeWakeUpAnimationWaiting(willPlay);
// Once changing this value, see if we should move the clock.
positionClockAndNotifications();
@@ -3378,11 +3383,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
ViewGroupFadeHelper.reset(mView);
}
- void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
}
- void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
@@ -3565,6 +3572,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
+ mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
mTrackingPointer = -1;
mAmbientState.setSwipingUp(false);
if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
@@ -3586,15 +3594,19 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
} else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
if (onKeyguard) {
expand = true;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
+ forceCancel, expand);
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
expand = !mPanelClosedOnDown;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel", forceCancel, expand);
}
} else {
expand = flingExpands(vel, vectorVel, x, y);
+ mShadeLog.logEndMotionEvent("endMotionEvent: flingExpands", forceCancel, expand);
}
mDozeLog.traceFling(
@@ -3847,8 +3859,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return !isFullyCollapsed() && !mTracking && !mClosing;
}
- /** Collapses the shade instantly without animation. */
- void instantCollapse() {
+ @Override
+ public void instantCollapse() {
abortAnimations();
setExpandedFraction(0f);
if (mExpanding) {
@@ -4021,8 +4033,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mFixedDuration = NO_FIXED_DURATION;
}
- /** */
- boolean postToView(Runnable action) {
+ @Override
+ public boolean postToView(Runnable action) {
return mView.post(action);
}
@@ -4731,6 +4743,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("intercept down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
@@ -4948,6 +4962,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("handle down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
@@ -5113,18 +5129,5 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return super.performAccessibilityAction(host, action, args);
}
}
-
- /** Listens for when touch tracking begins. */
- interface TrackingStartedListener {
- void onTrackingStarted();
- }
-
- /** Listens for when shade begins opening of finishes closing. */
- interface OpenCloseListener {
- /** Called when the shade finishes closing. */
- void onClosingFinished();
- /** Called when the shade starts opening. */
- void onOpenStarted();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 1361c9f25eff..025c461110ef 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -68,6 +68,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -98,7 +99,6 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
@@ -113,7 +113,7 @@ import javax.inject.Inject;
/** Handles QuickSettings touch handling, expansion and animation state
* TODO (b/264460656) make this dumpable
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public class QuickSettingsController implements Dumpable {
public static final String TAG = "QuickSettingsController";
@@ -1220,14 +1220,15 @@ public class QuickSettingsController implements Dumpable {
if (mIsFullWidth) {
clipStatusView = qsVisible;
float screenCornerRadius =
- !mSplitShadeEnabled || mRecordingController.isRecording()
- || mCastController.hasConnectedCastDevice()
+ mRecordingController.isRecording() || mCastController.hasConnectedCastDevice()
? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
- float bottomRadius = mExpanded ? screenCornerRadius :
- calculateBottomCornerRadius(screenCornerRadius);
+ float bottomRadius = mSplitShadeEnabled ? screenCornerRadius : 0;
+ if (!mExpanded) {
+ bottomRadius = calculateBottomCornerRadius(bottomRadius);
+ }
mScrimController.setNotificationBottomRadius(bottomRadius);
}
if (isQsFragmentCreated()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index 9ed0e9a8b359..317d88585958 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -165,8 +165,7 @@ public interface ShadeController {
NotificationShadeWindowViewController notificationShadeWindowViewController);
/** */
- void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController);
+ void setShadeViewController(ShadeViewController shadeViewController);
/** Listens for shade visibility changes. */
interface ShadeVisibilityListener {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index c9338b3614ea..b92afac047fa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -70,7 +70,8 @@ public final class ShadeControllerImpl implements ShadeController {
private boolean mExpandedVisible;
- private NotificationPanelViewController mNotificationPanelViewController;
+ // TODO(b/237661616): Rename this variable to mShadeViewController.
+ private ShadeViewController mNotificationPanelViewController;
private NotificationPresenter mPresenter;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private ShadeVisibilityListener mShadeVisibilityListener;
@@ -426,12 +427,11 @@ public final class ShadeControllerImpl implements ShadeController {
}
@Override
- public void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
+ public void setShadeViewController(ShadeViewController shadeViewController) {
+ mNotificationPanelViewController = shadeViewController;
mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {
ShadeControllerImpl.this.onClosingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 3af75cef3d4c..8789a8b3b7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -195,7 +195,9 @@ constructor(
set(value) {
if (visible && field != value) {
field = value
+ iconContainer.setQsExpansionTransitioning(value > 0f && value < 1.0f)
updatePosition()
+ updateIgnoredSlots()
}
}
@@ -216,6 +218,8 @@ constructor(
view.onApplyWindowInsets(insets)
}
+ private var singleCarrier = false
+
private val demoModeReceiver =
object : DemoMode {
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
@@ -479,17 +483,20 @@ constructor(
private fun updateListeners() {
mShadeCarrierGroupController.setListening(visible)
if (visible) {
- updateSingleCarrier(mShadeCarrierGroupController.isSingleCarrier)
+ singleCarrier = mShadeCarrierGroupController.isSingleCarrier
+ updateIgnoredSlots()
mShadeCarrierGroupController.setOnSingleCarrierChangedListener {
- updateSingleCarrier(it)
+ singleCarrier = it
+ updateIgnoredSlots()
}
} else {
mShadeCarrierGroupController.setOnSingleCarrierChangedListener(null)
}
}
- private fun updateSingleCarrier(singleCarrier: Boolean) {
- if (singleCarrier) {
+ private fun updateIgnoredSlots() {
+ // switching from QQS to QS state halfway through the transition
+ if (singleCarrier || qsExpandedFraction < 0.5) {
iconContainer.removeIgnoredSlots(carrierIconSlots)
} else {
iconContainer.addIgnoredSlots(carrierIconSlots)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 2da8d5f4d921..1c30bddfe859 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.shade
import android.view.MotionEvent
import com.android.systemui.log.dagger.ShadeLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.shade.ShadeViewController.Companion.FLING_COLLAPSE
import com.android.systemui.shade.ShadeViewController.Companion.FLING_EXPAND
import com.android.systemui.shade.ShadeViewController.Companion.FLING_HIDE
@@ -90,7 +90,7 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) {
double1 = event.y.toDouble()
},
{
- "$str1\neventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
+ "$str1: eventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
}
)
}
@@ -280,6 +280,42 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) {
)
}
+ fun logEndMotionEvent(
+ msg: String,
+ forceCancel: Boolean,
+ expand: Boolean,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = forceCancel
+ bool2 = expand
+ },
+ { "$str1; force=$bool1; expand=$bool2" }
+ )
+ }
+
+ fun logPanelClosedOnDown(
+ msg: String,
+ panelClosedOnDown: Boolean,
+ expandFraction: Float,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = panelClosedOnDown
+ double1 = expandFraction.toDouble()
+ },
+ { "$str1; mPanelClosedOnDown=$bool1; mExpandedFraction=$double1" }
+ )
+ }
+
fun flingQs(flingType: Int, isClick: Boolean) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 3686b7eda0e0..2c560c952732 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
import com.android.systemui.statusbar.policy.BatteryController
@@ -66,6 +67,12 @@ abstract class ShadeModule {
@ClassKey(AuthRippleController::class)
abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeViewController(
+ notificationPanelViewController: NotificationPanelViewController
+ ): ShadeViewController
+
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
@@ -147,6 +154,20 @@ abstract class ShadeModule {
return notificationShadeWindowView.findViewById(R.id.notification_panel)
}
+ /**
+ * Constructs a new, unattached [KeyguardBottomAreaView].
+ *
+ * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+ */
+ @Provides
+ fun providesKeyguardBottomAreaView(
+ npv: NotificationPanelView,
+ layoutInflater: LayoutInflater,
+ ): KeyguardBottomAreaView {
+ return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
+ as KeyguardBottomAreaView
+ }
+
@Provides
@SysUISingleton
fun providesLightRevealScrim(
@@ -176,9 +197,15 @@ abstract class ShadeModule {
@Provides
@SysUISingleton
fun providesLockIconView(
- notificationShadeWindowView: NotificationShadeWindowView,
+ keyguardRootView: KeyguardRootView,
+ notificationPanelView: NotificationPanelView,
+ featureFlags: FeatureFlags
): LockIconView {
- return notificationShadeWindowView.findViewById(R.id.lock_icon_view)
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ return keyguardRootView.findViewById(R.id.lock_icon_view)
+ } else {
+ return notificationPanelView.findViewById(R.id.lock_icon_view)
+ }
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 3d9fcf9cdecb..9aa5eb0cd68b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -17,6 +17,7 @@ package com.android.systemui.shade
import android.view.MotionEvent
import android.view.ViewGroup
+import android.view.ViewTreeObserver
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -77,6 +78,9 @@ interface ShadeViewController {
/** Collapses the shade with an animation duration in milliseconds. */
fun collapseWithDuration(animationDuration: Int)
+ /** Collapses the shade instantly without animation. */
+ fun instantCollapse()
+
/**
* Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If
* in split shade, it will collapse the whole shade.
@@ -100,6 +104,9 @@ interface ShadeViewController {
/** Returns whether the shade's top level view is enabled. */
val isViewEnabled: Boolean
+ /** Sets a listener to be notified when the shade starts opening or finishes closing. */
+ fun setOpenCloseListener(openCloseListener: OpenCloseListener)
+
/** Returns whether status bar icons should be hidden when the shade is expanded. */
fun shouldHideStatusBarIconsWhenExpanded(): Boolean
@@ -109,6 +116,9 @@ interface ShadeViewController {
*/
fun blockExpansionForCurrentTouch()
+ /** Sets a listener to be notified when touch tracking begins. */
+ fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
+
/**
* Disables the shade header.
*
@@ -178,6 +188,15 @@ interface ShadeViewController {
/** Ensures that the touchable region is updated. */
fun updateTouchableRegion()
+ /** Adds a global layout listener. */
+ fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Removes a global layout listener. */
+ fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Posts the given runnable to the view. */
+ fun postToView(action: Runnable): Boolean
+
// ******* Begin Keyguard Section *********
/** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
fun transitionToExpandedShade(delay: Long)
@@ -337,3 +356,17 @@ interface ShadeViewStateProvider {
/** Return the fraction of the shade that's expanded, when in lockscreen. */
val lockscreenShadeDragProgress: Float
}
+
+/** Listens for when touch tracking begins. */
+interface TrackingStartedListener {
+ fun onTrackingStarted()
+}
+
+/** Listens for when shade begins opening or finishes closing. */
+interface OpenCloseListener {
+ /** Called when the shade finishes closing. */
+ fun onClosingFinished()
+
+ /** Called when the shade starts opening. */
+ fun onOpenStarted()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
index d06634b63b6b..51a27cf8989a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
@@ -21,9 +21,9 @@ import com.android.systemui.log.dagger.ShadeWindowLog
import com.android.systemui.log.ConstantStringsLogger
import com.android.systemui.log.ConstantStringsLoggerImpl
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogMessage
import javax.inject.Inject
private const val TAG = "systemui.shadewindow"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index eceda8453902..6fde84a35fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -17,38 +17,52 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.domain.interactor.UserInteractor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Business logic for shade interactions. */
@SysUISingleton
class ShadeInteractor
@Inject
constructor(
+ @Application scope: CoroutineScope,
disableFlagsRepository: DisableFlagsRepository,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
) {
+ /** Emits true if the shade is currently allowed and false otherwise. */
+ val isShadeEnabled: StateFlow<Boolean> =
+ disableFlagsRepository.disableFlags
+ .map { it.isShadeEnabled() }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
disableFlagsRepository.disableFlags,
+ isShadeEnabled,
keyguardRepository.isDozing,
userSetupRepository.isUserSetupFlow,
- ) { disableFlags, isDozing, isUserSetup ->
+ ) { disableFlags, isShadeEnabled, isDozing, isUserSetup ->
deviceProvisionedController.isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
// use the lock screen user switcher, QS is not needed.)
(isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
- disableFlags.isShadeEnabled() &&
+ isShadeEnabled &&
disableFlags.isQuickSettingsEnabled() &&
!isDozing
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
index e008ec0dc75c..d3c19b75a71d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar
import android.app.PendingIntent
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6c2c0cf12aad..a532195c5b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
@@ -36,7 +37,7 @@ import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.os.Binder;
@@ -225,10 +226,8 @@ public class CommandQueue extends IStatusBar.Stub implements
* @param backDisposition Disposition mode of back button. It should be one of below flags:
* @param showImeSwitcher {@code true} to show IME switch button.
*/
- default void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher) { }
+ default void setImeWindowStatus(int displayId, IBinder token, int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
default void showRecentApps(boolean triggeredFromAltTab) { }
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
default void toggleTaskbar() { }
@@ -679,9 +678,7 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -1095,9 +1092,7 @@ public class CommandQueue extends IStatusBar.Stub implements
}
}
- private void handleShowImeButton(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
if (displayId == INVALID_DISPLAY) return;
@@ -1117,7 +1112,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private void sendImeInvisibleStatusForPrevNavBar() {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId,
- null /* token */, 0 /* vis */, BACK_DISPOSITION_DEFAULT,
+ null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT,
false /* showImeSwitcher */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 39181449aaa0..ec66e994b58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -557,19 +557,7 @@ public final class KeyboardShortcutListSearch {
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_access_google_assistant),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))),
- /* Lock screen: Meta + L */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_lock_screen),
- Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_L, KeyEvent.META_META_ON))),
- /* Pull up Notes app for quick memo: Meta + Ctrl + N */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_quick_memo),
- Arrays.asList(
- Pair.create(
- KeyEvent.KEYCODE_N,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON)))
+ Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON)))
);
for (ShortcutKeyGroupMultiMappingInfo info : infoList) {
systemGroup.addItem(info.getShortcutMultiMappingInfo());
@@ -611,21 +599,12 @@ public final class KeyboardShortcutListSearch {
new ArrayList<>());
// System multitasking shortcuts:
- // Enter Split screen with current app to RHS: Meta + Ctrl + Right arrow
- // Enter Split screen with current app to LHS: Meta + Ctrl + Left arrow
// Switch from Split screen to full screen: Meta + Ctrl + Up arrow
- // During Split screen: replace an app from one to another: Meta + Ctrl + Down arrow
String[] shortcutLabels = {
- context.getString(R.string.system_multitasking_rhs),
- context.getString(R.string.system_multitasking_lhs),
context.getString(R.string.system_multitasking_full_screen),
- context.getString(R.string.system_multitasking_replace)
};
int[] keyCodes = {
- KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_DOWN
};
for (int i = 0; i < shortcutLabels.length; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index a3fd82e9b140..ae3d41e19b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -440,10 +440,15 @@ public final class KeyboardShortcuts {
mContext.getString(R.string.keyboard_shortcut_group_system_back),
KeyEvent.KEYCODE_DEL,
KeyEvent.META_META_ON));
- systemGroup.addItem(new KeyboardShortcutInfo(
- mContext.getString(R.string.keyboard_shortcut_group_system_recents),
- KeyEvent.KEYCODE_TAB,
- KeyEvent.META_ALT_ON));
+
+ // Some devices (like TV) don't have recents
+ if (mContext.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(R.string.keyboard_shortcut_group_system_recents),
+ KeyEvent.KEYCODE_TAB,
+ KeyEvent.META_ALT_ON));
+ }
+
systemGroup.addItem(new KeyboardShortcutInfo(
mContext.getString(
R.string.keyboard_shortcut_group_system_notifications),
@@ -683,8 +688,10 @@ public final class KeyboardShortcuts {
ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
.findViewById(R.id.keyboard_shortcuts_item_container);
final int shortcutKeysSize = shortcutKeys.size();
+ final List<String> humanReadableShortcuts = new ArrayList<>();
for (int k = 0; k < shortcutKeysSize; k++) {
StringDrawableContainer shortcutRepresentation = shortcutKeys.get(k);
+ humanReadableShortcuts.add(shortcutRepresentation.mString);
if (shortcutRepresentation.mDrawable != null) {
ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
@@ -714,6 +721,11 @@ public final class KeyboardShortcuts {
shortcutItemsContainer.addView(shortcutKeyTextView);
}
}
+ CharSequence contentDescription = info.getLabel();
+ if (!humanReadableShortcuts.isEmpty()) {
+ contentDescription += ": " + String.join(", ", humanReadableShortcuts);
+ }
+ shortcutView.setContentDescription(contentDescription);
shortcutContainer.addView(shortcutView);
}
keyboardShortcutsLayout.addView(shortcutContainer);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 96924821cc1d..42ebaa3877d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -41,7 +41,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
-import static com.android.systemui.log.LogLevel.ERROR;
+import static com.android.systemui.log.core.LogLevel.ERROR;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import android.app.AlarmManager;
@@ -97,7 +97,7 @@ import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.util.IndicationHelper;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index e2d2ac0fcb58..4710574ac20d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -32,6 +32,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -76,6 +77,7 @@ class LockscreenShadeTransitionController @Inject constructor(
dumpManager: DumpManager,
qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory,
private val shadeRepository: ShadeRepository,
+ private val shadeInteractor: ShadeInteractor,
private val powerInteractor: PowerInteractor,
) : Dumpable {
private var pulseHeight: Float = 0f
@@ -558,7 +560,7 @@ class LockscreenShadeTransitionController @Inject constructor(
animationHandler: ((Long) -> Unit)? = null,
cancelAction: Runnable? = null
) {
- if (centralSurfaces.isShadeDisabled) {
+ if (!shadeInteractor.isShadeEnabled.value) {
cancelAction?.run()
logger.logShadeDisabledOnGoToLockedShade()
return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
new file mode 100644
index 000000000000..de369c35345c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+/**
+ * [CommandParser] defines the collection of tokens which can be parsed from an incoming command
+ * list, and parses them into their respective containers. Supported tokens are of the following
+ * forms:
+ * ```
+ * Flag: boolean value, false by default. always optional.
+ * Param: named parameter, taking N args all of a given type. Currently only single arg parameters
+ * are supported.
+ * SubCommand: named command created by adding a command to a parent. Supports all fields above, but
+ * not other subcommands.
+ * ```
+ *
+ * Tokens are added via the factory methods for each token type. They can be made `required` by
+ * calling the [require] method for the appropriate type, as follows:
+ * ```
+ * val requiredParam = parser.require(parser.param(...))
+ * ```
+ *
+ * The reason for having an explicit require is so that generic type arguments can be handled
+ * properly. See [SingleArgParam] and [SingleArgParamOptional] for the difference between an
+ * optional parameter and a required one.
+ *
+ * Typical usage of a required parameter, however, will occur within the context of a
+ * [ParseableCommand], which defines a convenience `require()` method:
+ * ```
+ * class MyCommand : ParseableCommand {
+ * val requiredParam = param(...).require()
+ * }
+ * ```
+ *
+ * This parser defines two modes of parsing, both of which validate for required parameters.
+ * 1. [parse] is a top-level parsing method. This parser will walk the given arg list and populate
+ * all of the delegate classes based on their type. It will handle SubCommands, and after parsing
+ * will check for any required-but-missing SubCommands or Params.
+ *
+ * **This method requires that every received token is represented in its grammar.**
+ * 2. [parseAsSubCommand] is a second-level parsing method suitable for any [SubCommand]. This
+ * method will handle _only_ flags and params. It will return parsing control to its parent
+ * parser on the first unknown token rather than throwing.
+ */
+class CommandParser {
+ private val _flags = mutableListOf<Flag>()
+ val flags: List<Flag> = _flags
+ private val _params = mutableListOf<Param>()
+ val params: List<Param> = _params
+ private val _subCommands = mutableListOf<SubCommand>()
+ val subCommands: List<SubCommand> = _subCommands
+
+ private val tokenSet = mutableSetOf<String>()
+
+ /**
+ * Parse the arg list into the fields defined in the containing class.
+ *
+ * @return true if all required fields are present after parsing
+ * @throws ArgParseError on any failure to process args
+ */
+ fun parse(args: List<String>): Boolean {
+ if (args.isEmpty()) {
+ return false
+ }
+
+ val iterator = args.listIterator()
+ var tokenHandled: Boolean
+ while (iterator.hasNext()) {
+ val token = iterator.next()
+ tokenHandled = false
+
+ flags
+ .find { it.matches(token) }
+ ?.let {
+ it.inner = true
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ params
+ .find { it.matches(token) }
+ ?.let {
+ it.parseArgsFromIter(iterator)
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ subCommands
+ .find { it.matches(token) }
+ ?.let {
+ it.parseSubCommandArgs(iterator)
+ tokenHandled = true
+ }
+
+ if (!tokenHandled) {
+ throw ArgParseError("Unknown token: $token")
+ }
+ }
+
+ return validateRequiredParams()
+ }
+
+ /**
+ * Parse a subset of the commands that came in from the top-level [parse] method, for the
+ * subcommand that this parser represents. Note that subcommands may not contain other
+ * subcommands. But they may contain flags and params.
+ *
+ * @return true if all required fields are present after parsing
+ * @throws ArgParseError on any failure to process args
+ */
+ fun parseAsSubCommand(iter: ListIterator<String>): Boolean {
+ // arg[-1] is our subcommand name, so the rest of the args are either for this
+ // subcommand, OR for the top-level command to handle. Therefore, we bail on the first
+ // failure, but still check our own required params
+
+ // The mere presence of a subcommand (similar to a flag) is a valid subcommand
+ if (flags.isEmpty() && params.isEmpty()) {
+ return validateRequiredParams()
+ }
+
+ var tokenHandled: Boolean
+ while (iter.hasNext()) {
+ val token = iter.next()
+ tokenHandled = false
+
+ flags
+ .find { it.matches(token) }
+ ?.let {
+ it.inner = true
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ params
+ .find { it.matches(token) }
+ ?.let {
+ it.parseArgsFromIter(iter)
+ tokenHandled = true
+ }
+
+ if (!tokenHandled) {
+ // Move the cursor position backwards since we've arrived at a token
+ // that we don't own
+ iter.previous()
+ break
+ }
+ }
+
+ return validateRequiredParams()
+ }
+
+ /**
+ * If [parse] or [parseAsSubCommand] does not produce a valid result, generate a list of errors
+ * based on missing elements
+ */
+ fun generateValidationErrorMessages(): List<String> {
+ val missingElements = mutableListOf<String>()
+
+ if (unhandledParams.isNotEmpty()) {
+ val names = unhandledParams.map { it.longName }
+ missingElements.add("No values passed for required params: $names")
+ }
+
+ if (unhandledSubCmds.isNotEmpty()) {
+ missingElements.addAll(unhandledSubCmds.map { it.longName })
+ val names = unhandledSubCmds.map { it.shortName }
+ missingElements.add("No values passed for required sub-commands: $names")
+ }
+
+ return missingElements
+ }
+
+ /** Check for any missing, required params, or any invalid subcommands */
+ private fun validateRequiredParams(): Boolean =
+ unhandledParams.isEmpty() && unhandledSubCmds.isEmpty() && unvalidatedSubCmds.isEmpty()
+
+ // If any required param (aka non-optional) hasn't handled a field, then return false
+ private val unhandledParams: List<Param>
+ get() = params.filter { (it is SingleArgParam<*>) && !it.handled }
+
+ private val unhandledSubCmds: List<SubCommand>
+ get() = subCommands.filter { (it is RequiredSubCommand<*> && !it.handled) }
+
+ private val unvalidatedSubCmds: List<SubCommand>
+ get() = subCommands.filter { !it.validationStatus }
+
+ private fun checkCliNames(short: String?, long: String): String? {
+ if (short != null && tokenSet.contains(short)) {
+ return short
+ }
+
+ if (tokenSet.contains(long)) {
+ return long
+ }
+
+ return null
+ }
+
+ private fun subCommandContainsSubCommands(cmd: ParseableCommand): Boolean =
+ cmd.parser.subCommands.isNotEmpty()
+
+ private fun registerNames(short: String?, long: String) {
+ if (short != null) {
+ tokenSet.add(short)
+ }
+ tokenSet.add(long)
+ }
+
+ /**
+ * Turns a [SingleArgParamOptional]<T> into a [SingleArgParam] by converting the [T?] into [T]
+ *
+ * @return a [SingleArgParam] property delegate
+ */
+ fun <T : Any> require(old: SingleArgParamOptional<T>): SingleArgParam<T> {
+ val newParam =
+ SingleArgParam(
+ longName = old.longName,
+ shortName = old.shortName,
+ description = old.description,
+ valueParser = old.valueParser,
+ )
+
+ replaceWithRequired(old, newParam)
+ return newParam
+ }
+
+ private fun <T : Any> replaceWithRequired(
+ old: SingleArgParamOptional<T>,
+ new: SingleArgParam<T>,
+ ) {
+ _params.remove(old)
+ _params.add(new)
+ }
+
+ /**
+ * Turns an [OptionalSubCommand] into a [RequiredSubCommand] by converting the [T?] in to [T]
+ *
+ * @return a [RequiredSubCommand] property delegate
+ */
+ fun <T : ParseableCommand> require(optional: OptionalSubCommand<T>): RequiredSubCommand<T> {
+ val newCmd = RequiredSubCommand(optional.cmd)
+ replaceWithRequired(optional, newCmd)
+ return newCmd
+ }
+
+ private fun <T : ParseableCommand> replaceWithRequired(
+ old: OptionalSubCommand<T>,
+ new: RequiredSubCommand<T>,
+ ) {
+ _subCommands.remove(old)
+ _subCommands.add(new)
+ }
+
+ internal fun flag(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ ): Flag {
+ checkCliNames(shortName, longName)?.let {
+ throw IllegalArgumentException("Detected reused flag name ($it)")
+ }
+ registerNames(shortName, longName)
+
+ val flag = Flag(shortName, longName, description)
+ _flags.add(flag)
+ return flag
+ }
+
+ internal fun <T : Any> param(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ valueParser: ValueParser<T>,
+ ): SingleArgParamOptional<T> {
+ checkCliNames(shortName, longName)?.let {
+ throw IllegalArgumentException("Detected reused param name ($it)")
+ }
+ registerNames(shortName, longName)
+
+ val param =
+ SingleArgParamOptional(
+ shortName = shortName,
+ longName = longName,
+ description = description,
+ valueParser = valueParser,
+ )
+ _params.add(param)
+ return param
+ }
+
+ internal fun <T : ParseableCommand> subCommand(
+ command: T,
+ ): OptionalSubCommand<T> {
+ checkCliNames(null, command.name)?.let {
+ throw IllegalArgumentException("Cannot re-use name for subcommand ($it)")
+ }
+
+ if (subCommandContainsSubCommands(command)) {
+ throw IllegalArgumentException(
+ "SubCommands may not contain other SubCommands. $command"
+ )
+ }
+
+ registerNames(null, command.name)
+
+ val subCmd = OptionalSubCommand(command)
+ _subCommands.add(subCmd)
+ return subCmd
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt
new file mode 100644
index 000000000000..6ed5eed79c82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Definitions for all parameter types usable by [ParseableCommand]. Parameters are command line
+ * tokens that accept a fixed number of arguments and convert them to a parsed type.
+ *
+ * Example:
+ * ```
+ * my_command --single-arg-param arg
+ * ```
+ *
+ * In the example, `my_command` is the name of the command, `--single-arg-param` is the parameter,
+ * and `arg` is the value parsed by that parameter into its eventual type.
+ *
+ * Note on generics: The intended usage for parameters is to be able to return the parsed type from
+ * the given command as a `val` via property delegation. For example, let's say we have a command
+ * that has one optional and one required parameter:
+ * ```
+ * class MyCommand : ParseableCommand {
+ * val requiredParam: Int by parser.param(...).required()
+ * val optionalParam: Int? by parser.param(...)
+ * }
+ * ```
+ *
+ * In order to make the simple `param` method return the correct type, we need to do two things:
+ * 1. Break out the generic type into 2 pieces (TParsed and T)
+ * 2. Create two different underlying Parameter subclasses to handle the property delegation. One
+ * handles `T?` and the other handles `T`. Note that in both cases, `TParsed` is always non-null
+ * since the value parsed from the argument will throw an exception if missing or if it cannot be
+ * parsed.
+ */
+
+/** A param type knows the number of arguments it expects */
+sealed interface Param : Describable {
+ val numArgs: Int
+
+ /**
+ * Consume [numArgs] items from the iterator and relay the result into its corresponding
+ * delegated type.
+ */
+ fun parseArgsFromIter(iterator: Iterator<String>)
+}
+
+/**
+ * Base class for required and optional SingleArgParam classes. For convenience, UnaryParam is
+ * defined as a [MultipleArgParam] where numArgs = 1. The benefit is that we can define the parsing
+ * in a single place, and yet on the client side we can unwrap the underlying list of params
+ * automatically.
+ */
+abstract class UnaryParamBase<out T, out TParsed : T>(val wrapped: MultipleArgParam<T, TParsed>) :
+ Param, ReadOnlyProperty<Any?, T> {
+ var handled = false
+
+ override fun describe(pw: IndentingPrintWriter) {
+ if (shortName != null) {
+ pw.print("$shortName, ")
+ }
+ pw.print(longName)
+ pw.println(" ${typeDescription()}")
+ if (description != null) {
+ pw.indented { pw.println(description) }
+ }
+ }
+
+ /**
+ * Try to describe the arg type. We can know if it's one of the base types what kind of input it
+ * takes. Otherwise just print "<arg>" and let the clients describe in the help text
+ */
+ private fun typeDescription() =
+ when (wrapped.valueParser) {
+ Type.Int -> "<int>"
+ Type.Float -> "<float>"
+ Type.String -> "<string>"
+ Type.Boolean -> "<boolean>"
+ else -> "<arg>"
+ }
+}
+
+/** Required single-arg parameter, delegating a non-null type to the client. */
+class SingleArgParam<out T : Any>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val description: String? = null,
+ val valueParser: ValueParser<T>,
+) :
+ UnaryParamBase<T, T>(
+ MultipleArgParam(
+ longName,
+ shortName,
+ 1,
+ description,
+ valueParser,
+ )
+ ) {
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T =
+ if (handled) {
+ wrapped.getValue(thisRef, property)[0]
+ } else {
+ throw IllegalStateException("Attempt to read property before parse() has executed")
+ }
+
+ override val numArgs: Int = 1
+
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ wrapped.parseArgsFromIter(iterator)
+ handled = true
+ }
+}
+
+/** Optional single-argument parameter, delegating a nullable type to the client. */
+class SingleArgParamOptional<out T : Any>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val description: String? = null,
+ val valueParser: ValueParser<T>,
+) :
+ UnaryParamBase<T?, T>(
+ MultipleArgParam(
+ longName,
+ shortName,
+ 1,
+ description,
+ valueParser,
+ )
+ ) {
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
+ wrapped.getValue(thisRef, property).getOrNull(0)
+
+ override val numArgs: Int = 1
+
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ wrapped.parseArgsFromIter(iterator)
+ handled = true
+ }
+}
+
+/**
+ * Parses a list of args into the underlying [T] data type. The resultant value is an ordered list
+ * of type [TParsed].
+ *
+ * [T] and [TParsed] are split out here in the case where the entire param is optional. I.e., a
+ * MultipleArgParam<T?, T> indicates a command line argument that can be omitted. In that case, the
+ * inner list is List<T>?, NOT List<T?>. If the argument is provided, then the type is always going
+ * to be parsed into T rather than T?.
+ */
+class MultipleArgParam<out T, out TParsed : T>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val numArgs: Int = 1,
+ override val description: String? = null,
+ val valueParser: ValueParser<TParsed>,
+) : ReadOnlyProperty<Any?, List<TParsed>>, Param {
+ private val inner: MutableList<TParsed> = mutableListOf()
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): List<TParsed> = inner
+
+ /**
+ * Consumes [numArgs] values of the iterator and parses them into [TParsed].
+ *
+ * @throws ArgParseError on the first failure
+ */
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ if (!iterator.hasNext()) {
+ throw ArgParseError("no argument provided for $shortName")
+ }
+ for (i in 0 until numArgs) {
+ valueParser
+ .parseValue(iterator.next())
+ .fold(onSuccess = { inner.add(it) }, onFailure = { throw it })
+ }
+ }
+}
+
+data class ArgParseError(override val message: String) : Exception(message)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt
new file mode 100644
index 000000000000..ecd3fa6cc299
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import java.io.PrintWriter
+import java.lang.IllegalArgumentException
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * An implementation of [Command] that includes a [CommandParser] which can set all delegated
+ * properties.
+ *
+ * As the number of registrants to [CommandRegistry] grows, we should have a default mechanism for
+ * parsing common command line arguments. We are not expecting to build an arbitrarily-functional
+ * CLI, nor a GNU arg parse compliant interface here, we simply want to be able to empower clients
+ * to create simple CLI grammars such as:
+ * ```
+ * $ my_command [-f|--flag]
+ * $ my_command [-a|--arg] <params...>
+ * $ my_command [subcommand1] [subcommand2]
+ * $ my_command <positional_arg ...> # not-yet implemented
+ * ```
+ *
+ * Note that the flags `-h` and `--help` are reserved for the base class. It seems prudent to just
+ * avoid them in your implementation.
+ *
+ * Usage:
+ *
+ * The intended usage tries to be clever enough to enable good ergonomics, while not too clever as
+ * to be unmaintainable. Using the default parser is done using property delegates, and looks like:
+ * ```
+ * class MyCommand(
+ * onExecute: (cmd: MyCommand, pw: PrintWriter) -> ()
+ * ) : ParseableCommand(name) {
+ * val flag1 by flag(
+ * shortName = "-f",
+ * longName = "--flag",
+ * required = false,
+ * )
+ * val param1: String by param(
+ * shortName = "-a",
+ * longName = "--args",
+ * valueParser = Type.String
+ * ).required()
+ * val param2: Int by param(..., valueParser = Type.Int)
+ * val subCommand by subCommand(...)
+ *
+ * override fun execute(pw: PrintWriter) {
+ * onExecute(this, pw)
+ * }
+ *
+ * companion object {
+ * const val name = "my_command"
+ * }
+ * }
+ *
+ * fun main() {
+ * fun printArgs(cmd: MyCommand, pw: PrintWriter) {
+ * pw.println("${cmd.flag1}")
+ * pw.println("${cmd.param1}")
+ * pw.println("${cmd.param2}")
+ * pw.println("${cmd.subCommand}")
+ * }
+ *
+ * commandRegistry.registerCommand(MyCommand.companion.name) {
+ * MyCommand() { (cmd, pw) ->
+ * printArgs(cmd, pw)
+ * }
+ * }
+ * }
+ *
+ * ```
+ */
+abstract class ParseableCommand(val name: String, val description: String? = null) : Command {
+ val parser: CommandParser = CommandParser()
+
+ val help by flag(longName = "help", shortName = "h", description = "Print help and return")
+
+ /**
+ * After [execute(pw, args)] is called, this class goes through a parsing stage and sets all
+ * delegated properties. It is safe to read any delegated properties here.
+ *
+ * This method is never called for [SubCommand]s, since they are associated with a top-level
+ * command that handles [execute]
+ */
+ abstract fun execute(pw: PrintWriter)
+
+ /**
+ * Given a command string list, [execute] parses the incoming command and validates the input.
+ * If this command or any of its subcommands is passed `-h` or `--help`, then execute will only
+ * print the relevant help message and exit.
+ *
+ * If any error is thrown during parsing, we will catch and log the error. This process should
+ * _never_ take down its process. Override [onParseFailed] to handle an [ArgParseError].
+ *
+ * Important: none of the delegated fields can be read before this stage.
+ */
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val success: Boolean
+ try {
+ success = parser.parse(args)
+ } catch (e: ArgParseError) {
+ pw.println(e.message)
+ onParseFailed(e)
+ return
+ } catch (e: Exception) {
+ pw.println("Unknown exception encountered during parse")
+ pw.println(e)
+ return
+ }
+
+ // Now we've parsed the incoming command without error. There are two things to check:
+ // 1. If any help is requested, print the help message and return
+ // 2. Otherwise, make sure required params have been passed in, and execute
+
+ val helpSubCmds = subCmdsRequestingHelp()
+
+ // Top-level help encapsulates subcommands. Otherwise, if _any_ subcommand requests
+ // help then defer to them. Else, just execute
+ if (help) {
+ help(pw)
+ } else if (helpSubCmds.isNotEmpty()) {
+ helpSubCmds.forEach { it.help(pw) }
+ } else {
+ if (!success) {
+ parser.generateValidationErrorMessages().forEach { pw.println(it) }
+ } else {
+ execute(pw)
+ }
+ }
+ }
+
+ /**
+ * Returns a list of all commands that asked for help. If non-empty, parsing will stop to print
+ * help. It is not guaranteed that delegates are fulfilled if help is requested
+ */
+ private fun subCmdsRequestingHelp(): List<ParseableCommand> =
+ parser.subCommands.filter { it.cmd.help }.map { it.cmd }
+
+ /** Override to do something when parsing fails */
+ open fun onParseFailed(error: ArgParseError) {}
+
+ /** Override to print a usage clause. E.g. `usage: my-cmd <arg1> <arg2>` */
+ open fun usage(pw: IndentingPrintWriter) {}
+
+ /**
+ * Print out the list of tokens, their received types if any, and their description in a
+ * formatted string.
+ *
+ * Example:
+ * ```
+ * my-command:
+ * MyCmd.description
+ *
+ * [optional] usage block
+ *
+ * Flags:
+ * -f
+ * description
+ * --flag2
+ * description
+ *
+ * Parameters:
+ * Required:
+ * -p1 [Param.Type]
+ * description
+ * --param2 [Param.Type]
+ * description
+ * Optional:
+ * same as above
+ *
+ * SubCommands:
+ * Required:
+ * ...
+ * Optional:
+ * ...
+ * ```
+ */
+ override fun help(pw: PrintWriter) {
+ val ipw = IndentingPrintWriter(pw)
+ ipw.printBoxed(name)
+ ipw.println()
+
+ // Allow for a simple `usage` block for clients
+ ipw.indented { usage(ipw) }
+
+ if (description != null) {
+ ipw.indented { ipw.println(description) }
+ ipw.println()
+ }
+
+ val flags = parser.flags
+ if (flags.isNotEmpty()) {
+ ipw.println("FLAGS:")
+ ipw.indented {
+ flags.forEach {
+ it.describe(ipw)
+ ipw.println()
+ }
+ }
+ }
+
+ val (required, optional) = parser.params.partition { it is SingleArgParam<*> }
+ if (required.isNotEmpty()) {
+ ipw.println("REQUIRED PARAMS:")
+ required.describe(ipw)
+ }
+ if (optional.isNotEmpty()) {
+ ipw.println("OPTIONAL PARAMS:")
+ optional.describe(ipw)
+ }
+
+ val (reqSub, optSub) = parser.subCommands.partition { it is RequiredSubCommand<*> }
+ if (reqSub.isNotEmpty()) {
+ ipw.println("REQUIRED SUBCOMMANDS:")
+ reqSub.describe(ipw)
+ }
+ if (optSub.isNotEmpty()) {
+ ipw.println("OPTIONAL SUBCOMMANDS:")
+ optSub.describe(ipw)
+ }
+ }
+
+ fun flag(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ ): Flag {
+ if (!checkShortName(shortName)) {
+ throw IllegalArgumentException(
+ "Flag short name must be one character long, or null. Got ($shortName)"
+ )
+ }
+
+ if (!checkLongName(longName)) {
+ throw IllegalArgumentException("Flags must not start with '-'. Got $($longName)")
+ }
+
+ val short = shortName?.let { "-$shortName" }
+ val long = "--$longName"
+
+ return parser.flag(long, short, description)
+ }
+
+ fun <T : Any> param(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ valueParser: ValueParser<T>,
+ ): SingleArgParamOptional<T> {
+ if (!checkShortName(shortName)) {
+ throw IllegalArgumentException(
+ "Parameter short name must be one character long, or null. Got ($shortName)"
+ )
+ }
+
+ if (!checkLongName(longName)) {
+ throw IllegalArgumentException("Parameters must not start with '-'. Got $($longName)")
+ }
+
+ val short = shortName?.let { "-$shortName" }
+ val long = "--$longName"
+
+ return parser.param(long, short, description, valueParser)
+ }
+
+ fun <T : ParseableCommand> subCommand(
+ command: T,
+ ) = parser.subCommand(command)
+
+ /** For use in conjunction with [param], makes the parameter required */
+ fun <T : Any> SingleArgParamOptional<T>.required(): SingleArgParam<T> = parser.require(this)
+
+ /** For use in conjunction with [subCommand], makes the given [SubCommand] required */
+ fun <T : ParseableCommand> OptionalSubCommand<T>.required(): RequiredSubCommand<T> =
+ parser.require(this)
+
+ private fun checkShortName(short: String?): Boolean {
+ return short == null || short.length == 1
+ }
+
+ private fun checkLongName(long: String): Boolean {
+ return !long.startsWith("-")
+ }
+
+ companion object {
+ fun Iterable<Describable>.describe(pw: IndentingPrintWriter) {
+ pw.indented {
+ forEach {
+ it.describe(pw)
+ pw.println()
+ }
+ }
+ }
+ }
+}
+
+/**
+ * A flag is a boolean value passed over the command line. It can have a short form or long form.
+ * The value is [Boolean.true] if the flag is found, else false
+ */
+data class Flag(
+ override val shortName: String? = null,
+ override val longName: String,
+ override val description: String? = null,
+) : ReadOnlyProperty<Any?, Boolean>, Describable {
+ var inner: Boolean = false
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>) = inner
+}
+
+/**
+ * Named CLI token. Can have a short or long name. Note: consider renaming to "primary" and
+ * "secondary" names since we don't actually care what the strings are
+ *
+ * Flags and params will have [shortName]s that are always prefixed with a single dash, while
+ * [longName]s are prefixed by a double dash. E.g., `my_command -f --flag`.
+ *
+ * Subcommands do not do any prefixing, and register their name as the [longName]
+ *
+ * Can be matched against an incoming token
+ */
+interface CliNamed {
+ val shortName: String?
+ val longName: String
+
+ fun matches(token: String) = shortName == token || longName == token
+}
+
+interface Describable : CliNamed {
+ val description: String?
+
+ fun describe(pw: IndentingPrintWriter) {
+ if (shortName != null) {
+ pw.print("$shortName, ")
+ }
+ pw.print(longName)
+ pw.println()
+ if (description != null) {
+ pw.indented { pw.println(description) }
+ }
+ }
+}
+
+/**
+ * Print [s] inside of a unicode character box, like so:
+ * ```
+ * ╔═══════════╗
+ * ║ my-string ║
+ * ╚═══════════╝
+ * ```
+ */
+fun PrintWriter.printDoubleBoxed(s: String) {
+ val length = s.length
+ println("╔${"═".repeat(length + 2)}╗")
+ println("║ $s ║")
+ println("╚${"═".repeat(length + 2)}╝")
+}
+
+/**
+ * Print [s] inside of a unicode character box, like so:
+ * ```
+ * ┌───────────┐
+ * │ my-string │
+ * └───────────┘
+ * ```
+ */
+fun PrintWriter.printBoxed(s: String) {
+ val length = s.length
+ println("┌${"─".repeat(length + 2)}┐")
+ println("│ $s │")
+ println("└${"─".repeat(length + 2)}┘")
+}
+
+fun IndentingPrintWriter.indented(block: () -> Unit) {
+ increaseIndent()
+ block()
+ decreaseIndent()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt
new file mode 100644
index 000000000000..41bac86fd6c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Sub commands wrap [ParseableCommand]s and are attached to a parent [ParseableCommand]. As such
+ * they have their own parser which will parse the args as a subcommand. I.e., the subcommand's
+ * parser will consume the iterator created by the parent, reversing the index when it reaches an
+ * unknown token.
+ *
+ * In order to keep subcommands relatively simple and not have to do complicated validation, sub
+ * commands will return control to the parent parser as soon as they discover a token that they do
+ * not own. They will throw an [ArgParseError] if parsing fails or if they don't receive arguments
+ * for a required parameter.
+ */
+sealed interface SubCommand : Describable {
+ val cmd: ParseableCommand
+
+ /** Checks if all of the required elements were passed in to [parseSubCommandArgs] */
+ var validationStatus: Boolean
+
+ /**
+ * To keep parsing simple, [parseSubCommandArgs] requires a [ListIterator] so that it can rewind
+ * the iterator when it yields control upwards
+ */
+ fun parseSubCommandArgs(iterator: ListIterator<String>)
+}
+
+/**
+ * Note that the delegated type from the subcommand is `T: ParseableCommand?`. SubCommands are
+ * created via adding a fully-formed [ParseableCommand] to parent command.
+ *
+ * At this point in time, I don't recommend nesting subcommands.
+ */
+class OptionalSubCommand<T : ParseableCommand>(
+ override val cmd: T,
+) : SubCommand, ReadOnlyProperty<Any?, ParseableCommand?> {
+ override val shortName: String? = null
+ override val longName: String = cmd.name
+ override val description: String? = cmd.description
+ override var validationStatus = true
+
+ private var isPresent = false
+
+ /** Consume tokens from the iterator and pass them to the wrapped command */
+ override fun parseSubCommandArgs(iterator: ListIterator<String>) {
+ validationStatus = cmd.parser.parseAsSubCommand(iterator)
+ isPresent = true
+ }
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
+ if (isPresent) {
+ cmd
+ } else {
+ null
+ }
+
+ override fun describe(pw: IndentingPrintWriter) {
+ cmd.help(pw)
+ }
+}
+
+/**
+ * Non-optional subcommand impl. Top-level parser is expected to throw [ArgParseError] if this token
+ * is not present in the incoming command
+ */
+class RequiredSubCommand<T : ParseableCommand>(
+ override val cmd: T,
+) : SubCommand, ReadOnlyProperty<Any?, ParseableCommand> {
+ override val shortName: String? = null
+ override val longName: String = cmd.name
+ override val description: String? = cmd.description
+ override var validationStatus = true
+
+ /** Unhandled, required subcommands are an error */
+ var handled = false
+
+ override fun parseSubCommandArgs(iterator: ListIterator<String>) {
+ validationStatus = cmd.parser.parseAsSubCommand(iterator)
+ handled = true
+ }
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): ParseableCommand = cmd
+
+ override fun describe(pw: IndentingPrintWriter) {
+ cmd.help(pw)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
new file mode 100644
index 000000000000..01083d9a7907
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+
+/**
+ * Utilities for parsing the [String] command line arguments. Arguments are related to the
+ * [Parameter] type, which declares the number of, and resulting type of, the arguments that it
+ * takes when parsing. For Example:
+ * ```
+ * my-command --param <str> --param2 <int>
+ * ```
+ *
+ * Defines 2 parameters, the first of which takes a string, and the second requires an int. Because
+ * fundamentally _everything_ is a string, we have to define a convenient way to get from the
+ * incoming `StringArg` to the resulting `T`-arg, where `T` is the type required by the client.
+ *
+ * Parsing is therefore a relatively straightforward operation: (String) -> T. However, since
+ * parsing can always fail, the type is actually (String) -> Result<T>. We will always want to fail
+ * on the first error and propagate it to the caller (typically this results in printing the `help`
+ * message of the command`).
+ *
+ * The identity parsing is trivial:
+ * ```
+ * (s: String) -> String = { s -> s }
+ * ```
+ *
+ * Basic mappings are actually even provided by Kotlin's stdlib:
+ * ```
+ * (s: String) -> Boolean = { s -> s.toBooleanOrNull() }
+ * (s: String) -> Int = { s -> s.toIntOrNull() }
+ * ...
+ * ```
+ *
+ * In order to properly encode errors, we will ascribe an error type to any `null` values, such that
+ * parsing looks like this:
+ * ```
+ * val mapping: (String) -> T? = {...} // for some T
+ * val parser: (String) -> Result<T> = { s ->
+ * mapping(s)?.let {
+ * Result.success(it)
+ * } ?: Result.failure(/* some failure type */)
+ * }
+ * ```
+ *
+ * Composition
+ *
+ * The ability to compose value parsing enables us to provide a couple of reasonable default parsers
+ * and allow clients to seamlessly build upon that using map functions. Consider the case where we
+ * want to validate that a value is an [Int] between 0 and 100. We start with the generic [Int]
+ * parser, and a validator, of the type (Int) -> Result<Int>:
+ * ```
+ * val intParser = { s ->
+ * s.toStringOrNull().?let {...} ?: ...
+ * }
+ *
+ * val validator = { i ->
+ * if (i > 100 || i < 0) {
+ * Result.failure(...)
+ * } else {
+ * Result.success(i)
+ * }
+ * ```
+ *
+ * In order to combine these functions, we need to define a new [flatMap] function that can get us
+ * from a `Result<T>` to a `Result<R>`, and short-circuit on any error. We want to see this:
+ * ```
+ * val validatingParser = { s ->
+ * intParser.invoke(s).flatMap { i ->
+ * validator(i)
+ * }
+ * }
+ * ```
+ *
+ * The flatMap is relatively simply defined, we can mimic the existing definition for [Result.map],
+ * though the implementation is uglier because of the `internal` definition for `value`
+ *
+ * ```
+ * inline fun <R, T> Result<T>.flatMap(transform: (value: T) -> Result<R>): Result<R> {
+ * return when {
+ * isSuccess -> transform(getOrThrow())
+ * else -> Result.failure(exceptionOrNull()!!)
+ * }
+ * }
+ * ```
+ */
+
+/**
+ * Given a [transform] that returns a [Result], apply the transform to this result, unwrapping the
+ * return value so that
+ *
+ * These [contract] and [callsInPlace] methods are copied from the [Result.map] definition
+ */
+@OptIn(ExperimentalContracts::class)
+inline fun <R, T> Result<T>.flatMap(transform: (value: T) -> Result<R>): Result<R> {
+ contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) }
+
+ return when {
+ // Should never throw, we just don't have access to [this.value]
+ isSuccess -> transform(getOrThrow())
+ // Exception should never be null here
+ else -> Result.failure(exceptionOrNull()!!)
+ }
+}
+
+/**
+ * ValueParser turns a [String] into a Result<A> by applying a transform. See the default
+ * implementations below for starting points. The intention here is to provide the base mappings and
+ * allow clients to attach their own transforms. They are expected to succeed or return null on
+ * failure. The failure is propagated to the command parser as a Result and will fail on any
+ * [Result.failure]
+ */
+fun interface ValueParser<out A> {
+ fun parseValue(value: String): Result<A>
+}
+
+/** Map a [ValueParser] of type A to one of type B, by applying the given [transform] */
+inline fun <A, B> ValueParser<A>.map(crossinline transform: (A) -> B?): ValueParser<B> {
+ return ValueParser<B> { value ->
+ this.parseValue(value).flatMap { a ->
+ transform(a)?.let { b -> Result.success(b) }
+ ?: Result.failure(ArgParseError("Failed to transform value $value"))
+ }
+ }
+}
+
+/**
+ * Base type parsers are provided by the lib, and can be simply composed upon by [ValueParser.map]
+ * functions on the parser
+ */
+
+/** String parsing always succeeds if the value exists */
+private val parseString: ValueParser<String> = ValueParser { value -> Result.success(value) }
+
+private val parseBoolean: ValueParser<Boolean> = ValueParser { value ->
+ value.toBooleanStrictOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as a boolean"))
+}
+
+private val parseInt: ValueParser<Int> = ValueParser { value ->
+ value.toIntOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as an int"))
+}
+
+private val parseFloat: ValueParser<Float> = ValueParser { value ->
+ value.toFloatOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as a float"))
+}
+
+/** Default parsers that can be use as-is, or [map]ped to another type */
+object Type {
+ val Boolean = parseBoolean
+ val Int = parseInt
+ val Float = parseFloat
+ val String = parseString
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 2465c21c956f..73f181b8c734 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -74,7 +74,7 @@ import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 075b41b91d97..035fa0454bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -41,6 +41,8 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -273,6 +275,21 @@ public interface CentralSurfacesDependenciesModule {
return ongoingCallController;
}
+ /**
+ * {@link NotificationPanelViewController} implements two interfaces:
+ * - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
+ * needing access to the shade.
+ * - {@link ShadeSurface}, which should *only* be used by {@link CentralSurfacesImpl}.
+ *
+ * Since {@link ShadeSurface} should only be accessible by {@link CentralSurfacesImpl}, it's
+ * *only* bound in this CentralSurfaces dependencies module.
+ * The {@link com.android.systemui.shade.ShadeViewController} interface is bound in
+ * {@link com.android.systemui.shade.ShadeModule} so others can access it.
+ */
+ @Binds
+ @SysUISingleton
+ ShadeSurface provideShadeSurface(NotificationPanelViewController impl);
+
/** */
@Binds
ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
index ac05248a2b87..2bb476523cb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
@@ -20,7 +20,7 @@ import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.app.StatusBarManager.DISABLE_NONE
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
index 3d6d48917dd3..84796f9acbc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
@@ -22,6 +22,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
@@ -36,6 +38,13 @@ interface StatusBarEventsModule {
@Provides
@SysUISingleton
+ @SystemStatusAnimationSchedulerLog
+ fun provideSystemStatusAnimationSchedulerLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("SystemStatusAnimationSchedulerLog", 60)
+ }
+
+ @Provides
+ @SysUISingleton
fun provideSystemStatusAnimationScheduler(
featureFlags: FeatureFlags,
coordinator: SystemEventCoordinator,
@@ -44,7 +53,8 @@ interface StatusBarEventsModule {
dumpManager: DumpManager,
systemClock: SystemClock,
@Application coroutineScope: CoroutineScope,
- @Main executor: DelayableExecutor
+ @Main executor: DelayableExecutor,
+ logger: SystemStatusAnimationSchedulerLogger
): SystemStatusAnimationScheduler {
return if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
SystemStatusAnimationSchedulerImpl(
@@ -53,7 +63,8 @@ interface StatusBarEventsModule {
statusBarWindowController,
dumpManager,
systemClock,
- coroutineScope
+ coroutineScope,
+ logger
)
} else {
SystemStatusAnimationSchedulerLegacyImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index 56ea703668d0..6fc715a2b578 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.events
import android.os.Process
import android.provider.DeviceConfig
-import android.util.Log
import androidx.core.animation.Animator
import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.AnimatorSet
@@ -69,7 +68,8 @@ constructor(
private val statusBarWindowController: StatusBarWindowController,
dumpManager: DumpManager,
private val systemClock: SystemClock,
- @Application private val coroutineScope: CoroutineScope
+ @Application private val coroutineScope: CoroutineScope,
+ private val logger: SystemStatusAnimationSchedulerLogger?
) : SystemStatusAnimationScheduler {
companion object {
@@ -121,6 +121,10 @@ constructor(
}
}
}
+
+ coroutineScope.launch {
+ animationState.collect { logger?.logAnimationStateUpdate(it) }
+ }
}
@SystemAnimationState override fun getAnimationState(): Int = animationState.value
@@ -140,32 +144,17 @@ constructor(
) {
// a event can only be scheduled if no other event is in progress or it has a higher
// priority. If a persistent dot is currently displayed, don't schedule the event.
- if (DEBUG) {
- Log.d(TAG, "scheduling event $event")
- }
-
+ logger?.logScheduleEvent(event)
scheduleEvent(event)
} else if (currentlyDisplayedEvent?.shouldUpdateFromEvent(event) == true) {
- if (DEBUG) {
- Log.d(
- TAG,
- "updating current event from: $event. animationState=${animationState.value}"
- )
- }
+ logger?.logUpdateEvent(event, animationState.value)
currentlyDisplayedEvent?.updateFromEvent(event)
if (event.forceVisible) hasPersistentDot = true
} else if (scheduledEvent.value?.shouldUpdateFromEvent(event) == true) {
- if (DEBUG) {
- Log.d(
- TAG,
- "updating scheduled event from: $event. animationState=${animationState.value}"
- )
- }
+ logger?.logUpdateEvent(event, animationState.value)
scheduledEvent.value?.updateFromEvent(event)
} else {
- if (DEBUG) {
- Log.d(TAG, "ignoring event $event")
- }
+ logger?.logIgnoreEvent(event)
}
}
@@ -356,6 +345,7 @@ constructor(
}
private fun notifyTransitionToPersistentDot(): Animator? {
+ logger?.logTransitionToPersistentDotCallbackInvoked()
val anims: List<Animator> =
listeners.mapNotNull {
it.onSystemStatusAnimationTransitionToPersistentDot(
@@ -373,6 +363,7 @@ constructor(
private fun notifyHidePersistentDot(): Animator? {
Assert.isMainThread()
+ logger?.logHidePersistentDotCallbackInvoked()
val anims: List<Animator> = listeners.mapNotNull { it.onHidePersistentDot() }
if (animationState.value == SHOWING_PERSISTENT_DOT) {
@@ -424,5 +415,4 @@ constructor(
}
}
-private const val DEBUG = false
private const val TAG = "SystemStatusAnimationSchedulerImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt
new file mode 100644
index 000000000000..4ac94a60d4c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import javax.inject.Qualifier
+
+/** Logs for the SystemStatusAnimationScheduler. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class SystemStatusAnimationSchedulerLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt
new file mode 100644
index 000000000000..22b0b691ad3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.statusbar.events
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import javax.inject.Inject
+
+/** Logs for the SystemStatusAnimationScheduler. */
+@SysUISingleton
+class SystemStatusAnimationSchedulerLogger
+@Inject
+constructor(
+ @SystemStatusAnimationSchedulerLog private val logBuffer: LogBuffer,
+) {
+
+ fun logScheduleEvent(event: StatusEvent) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ },
+ { "Scheduling event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }
+ )
+ }
+
+ fun logUpdateEvent(event: StatusEvent, @SystemAnimationState animationState: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ int2 = animationState
+ },
+ {
+ "Updating current event from: $str1(forceVisible=$bool1, priority=$int1, " +
+ "showAnimation=$bool2), animationState=${animationState.name()}"
+ }
+ )
+ }
+
+ fun logIgnoreEvent(event: StatusEvent) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ },
+ { "Ignore event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }
+ )
+ }
+
+ fun logHidePersistentDotCallbackInvoked() {
+ logBuffer.log(TAG, LogLevel.DEBUG, "Hide persistent dot callback invoked")
+ }
+
+ fun logTransitionToPersistentDotCallbackInvoked() {
+ logBuffer.log(TAG, LogLevel.DEBUG, "Transition to persistent dot callback invoked")
+ }
+
+ fun logAnimationStateUpdate(@SystemAnimationState animationState: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { int1 = animationState },
+ { "AnimationState update: ${int1.name()}" }
+ )
+ animationState.name()
+ }
+
+ private fun @receiver:SystemAnimationState Int.name() =
+ when (this) {
+ IDLE -> "IDLE"
+ ANIMATION_QUEUED -> "ANIMATION_QUEUED"
+ ANIMATING_IN -> "ANIMATING_IN"
+ RUNNING_CHIP_ANIM -> "RUNNING_CHIP_ANIM"
+ ANIMATING_OUT -> "ANIMATING_OUT"
+ SHOWING_PERSISTENT_DOT -> "SHOWING_PERSISTENT_DOT"
+ else -> "UNKNOWN_ANIMATION_STATE"
+ }
+}
+
+private const val TAG = "SystemStatusAnimationSchedulerLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
index a67c26c06cb9..96725fc09d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.gesture
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.SwipeUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [SwipeUpGestureHandler]. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a7496030b965..94251ffc74c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -124,6 +124,7 @@ constructor(
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
+ private var mSplitShadeEnabled = false
// TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
// how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
@@ -131,6 +132,7 @@ constructor(
// TODO: Move logic into SmartspaceView
var stateChangeListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
+ (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled)
smartspaceViews.add(v as SmartspaceView)
connectSession()
@@ -221,6 +223,11 @@ constructor(
execution.assertIsMainThread()
smartspaceViews.forEach { it.setDozeAmount(eased) }
}
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ execution.assertIsMainThread()
+ smartspaceViews.forEach { it.setDozing(isDozing) }
+ }
}
private val deviceProvisionedListener =
@@ -427,6 +434,11 @@ constructor(
reloadSmartspace()
}
+ fun setSplitShadeEnabled(enabled: Boolean) {
+ mSplitShadeEnabled = enabled
+ smartspaceViews.forEach { it.setSplitShadeEnabled(enabled) }
+ }
+
/**
* Requests the smartspace session for an update.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
index a3a72d92c2e4..cea2b595f595 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
index f7679ed058c6..502e1d9ea639 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -14,7 +14,7 @@
package com.android.systemui.statusbar.notification
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.NotificationLockscreenLog
import com.android.systemui.statusbar.StatusBarState
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
index 487a5f87d0bd..7809eaafe0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.NotificationRemoteInputLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index 39d0833c57d4..0ab348d174b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coalescer
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
class GroupCoalescerLogger @Inject constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
index 79c63e6b0db1..bd1141e79278 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
@@ -2,7 +2,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.row.NotificationGuts
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
index e17ce5cff37d..496fb83c1cf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -4,7 +4,7 @@ import android.util.Log
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "HeadsUpCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
index 1f8ec3411bcd..4c33524346eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.UnseenNotificationLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index 6271d38f1efa..bf65043ea534 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
index 1f4861a10e75..0d9681f801f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "ShadeEventCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index f13ff6814df8..a8409d0c6fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -18,9 +18,9 @@ package com.android.systemui.statusbar.notification.collection.listbuilder
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 73227ab9f4fb..014ac549591e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -22,11 +22,11 @@ import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import com.android.systemui.log.dagger.NotificationLog
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.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.WTF
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
index 07fd349d3786..e61f9bdda3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.util.Compile
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index a880b7157708..082f308bc731 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import java.lang.RuntimeException
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index 0b31265963ed..c6d2861a8c68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -2,7 +2,7 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 5bac2a9350a5..4a823a40a272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -20,9 +20,9 @@ import android.util.Log
import com.android.systemui.log.dagger.NotificationInterruptLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.util.Compile
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
index fe03b2ad6a32..0e1f66f7cbd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.logging
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8af488ea443d..27510d47b5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -359,9 +359,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
@Override
- public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
- Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 30747db0fb64..b34c28163abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2975,7 +2975,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
- float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2986,7 +2985,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void onAnimationEnd(Animator animation) {
ExpandableNotificationRow.super.performRemoveAnimation(
duration, delay, translationDirection, isHeadsUpAnimation,
- endLocation, onFinishedRunnable, animationListener);
+ onFinishedRunnable, animationListener);
}
});
anim.start();
@@ -2994,7 +2993,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
return super.performRemoveAnimation(duration, delay, translationDirection,
- isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+ isHeadsUpAnimation, onFinishedRunnable, animationListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index f0e15c27b7a7..f98624409e56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -367,7 +367,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
* such that the child appears to be going away to the top. 1
* Should mean the opposite.
* @param isHeadsUpAnimation Is this a headsUp animation.
- * @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
*
@@ -375,7 +374,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
* animation starts.
*/
public abstract long performRemoveAnimation(long duration,
- long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ long delay, float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
index 45be0b151870..385670080f63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.row
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index 89338f9eeed3..4f5a04f2bdc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.row
import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
index 684a276ed635..02627fd8f975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.row
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index b24cec150941..0c686be0406d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -235,7 +235,7 @@ public abstract class StackScrollerDecorView extends ExpandableView {
@Override
public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index b8f28b5a60ea..04308b47abc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -69,11 +69,14 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie
canvas.clipPath(clipPath)
}
-
- override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float,
- isHeadsUpAnimation: Boolean, endLocation: Float,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?): Long {
+ override fun performRemoveAnimation(
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?
+ ): Long {
return 0
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
index 6be1ef8711df..4986b632472d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.stack
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
index f9531876e30d..2da55828e30d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.stack
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.NotificationSectionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "NotifSections"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7c66bbe548a5..ef7375aa690b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -424,7 +424,8 @@ public class NotificationStackScrollLayoutController {
}
};
- private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+ @VisibleForTesting
+ final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
new NotificationSwipeHelper.NotificationCallback() {
@Override
@@ -483,10 +484,11 @@ public class NotificationStackScrollLayoutController {
*/
public void handleChildViewDismissed(View view) {
+ // The View needs to clean up the Swipe states, e.g. roundness.
+ mView.onSwipeEnd();
if (mView.getClearAllInProgress()) {
return;
}
- mView.onSwipeEnd();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
index 9c1bd174107e..2c38b8dfca04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -1,11 +1,11 @@
package com.android.systemui.statusbar.notification.stack
import android.view.ViewGroup
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d73919b82c42..2742a23d5fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,8 +27,6 @@ import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -427,7 +425,7 @@ public class StackStateAnimator {
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, postAnimation, null);
+ postAnimation, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (mHostLayout.isFullySwipedOut(changingView)) {
@@ -474,28 +472,12 @@ public class StackStateAnimator {
mTmpState.initFrom(changingView);
endRunnable = changingView::removeFromTransientContainer;
}
- float targetLocation = 0;
boolean needsAnimation = true;
if (changingView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
if (row.isDismissed()) {
needsAnimation = false;
}
-
- NotificationEntry entry = row.getEntry();
- StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
- final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
- if (centeredIcon != null && centeredIcon.getParent() != null) {
- icon = centeredIcon;
- }
- if (icon.getParent() != null) {
- icon.getLocationOnScreen(mTmpLocation);
- float iconPosition = mTmpLocation[0] - icon.getTranslationX()
- + ViewState.getFinalTranslationX(icon)
- + icon.getWidth() * 0.25f;
- mHostLayout.getLocationOnScreen(mTmpLocation);
- targetLocation = iconPosition - mTmpLocation[0];
- }
}
if (needsAnimation) {
@@ -515,7 +497,7 @@ public class StackStateAnimator {
}
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
- 0, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+ 0, 0.0f, true /* isHeadsUpAppear */,
postAnimation, getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index c7f80f311650..0b2c4863157c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.notification.stack
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 4ba09e175b8b..478baf2f58d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -203,8 +203,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
int getStatusBarHeight();
- boolean isShadeDisabled();
-
boolean isLaunchingActivityOverLockscreen();
void onKeyguardViewManagerStatesUpdated();
@@ -380,14 +378,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void resendMessage(Object msg);
- int getDisabled1();
-
- void setDisabled1(int disabled);
-
- int getDisabled2();
-
- void setDisabled2(int disabled);
-
void setLastCameraLaunchSource(int source);
void setLaunchCameraOnFinishedGoingToSleep(boolean launch);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 332be2aa957d..6431ef958239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -111,6 +111,9 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private int mDisabled1;
+ private int mDisabled2;
+
@Inject
CentralSurfacesCommandQueueCallbacks(
CentralSurfaces centralSurfaces,
@@ -256,22 +259,14 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
return;
}
- int state2BeforeAdjustment = state2;
- state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- Log.d(CentralSurfaces.TAG,
- mDisableFlagsLogger.getDisableFlagsString(
- /* new= */ new DisableFlagsLogger.DisableState(
- state1, state2BeforeAdjustment),
- /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
- state1, state2)));
-
- final int old1 = mCentralSurfaces.getDisabled1();
+ final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
- mCentralSurfaces.setDisabled1(state1);
+ mDisabled1 = state1;
- final int old2 = mCentralSurfaces.getDisabled2();
+ state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+ final int old2 = mDisabled2;
final int diff2 = state2 ^ old2;
- mCentralSurfaces.setDisabled2(state2);
+ mDisabled2 = state2;
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 88ccae624dd0..0d3dfaeb85b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -182,7 +182,6 @@ import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
@@ -363,26 +362,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public int getDisabled1() {
- return mDisabled1;
- }
-
- @Override
- public void setDisabled1(int disabled) {
- mDisabled1 = disabled;
- }
-
- @Override
- public int getDisabled2() {
- return mDisabled2;
- }
-
- @Override
- public void setDisabled2(int disabled) {
- mDisabled2 = disabled;
- }
-
- @Override
public void setLastCameraLaunchSource(int source) {
mLastCameraLaunchSource = source;
}
@@ -497,14 +476,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
- @VisibleForTesting
- ShadeSurface mShadeSurface;
+ private final ShadeSurface mShadeSurface;
private final ShadeLogger mShadeLogger;
// settings
private QSPanelController mQSPanelController;
- @VisibleForTesting
- QuickSettingsController mQsController;
+ private final QuickSettingsController mQsController;
KeyguardIndicationController mKeyguardIndicationController;
@@ -530,11 +507,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private CentralSurfacesComponent mCentralSurfacesComponent;
- // Flags for disabling the status bar
- // Two variables because the first one evidently ran out of room for new flags.
- private int mDisabled1 = 0;
- private int mDisabled2 = 0;
-
/**
* This keeps track of whether we have (or haven't) registered the predictive back callback.
* Since we can have visible -> visible transitions, we need to avoid
@@ -729,9 +701,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
@UiBackground Executor uiBgExecutor,
+ ShadeSurface shadeSurface,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
+ QuickSettingsController quickSettingsController,
UserSwitcherController userSwitcherController,
BatteryController batteryController,
SysuiColorExtractor colorExtractor,
@@ -830,9 +804,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mMetricsLogger = metricsLogger;
mShadeLogger = shadeLogger;
mUiBgExecutor = uiBgExecutor;
+ mShadeSurface = shadeSurface;
mMediaManager = notificationMediaManager;
mLockscreenUserManager = lockScreenUserManager;
mRemoteInputManager = remoteInputManager;
+ mQsController = quickSettingsController;
mUserSwitcherController = userSwitcherController;
mBatteryController = batteryController;
mColorExtractor = colorExtractor;
@@ -1636,13 +1612,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// (Right now, there's a circular dependency.)
mNotificationShadeWindowController.setWindowRootView(windowRootView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- NotificationPanelViewController npvc =
- mCentralSurfacesComponent.getNotificationPanelViewController();
- mShadeSurface = npvc;
- mShadeController.setNotificationPanelViewController(npvc);
+ mShadeController.setShadeViewController(mShadeSurface);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
- mQsController = mCentralSurfacesComponent.getQuickSettingsController();
mBackActionInteractor.setup(mQsController, mShadeSurface);
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
@@ -1724,11 +1696,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return mStatusBarWindowController.getStatusBarHeight();
}
- @Override
- public boolean isShadeDisabled() {
- return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
- }
-
private void updateReportRejectedTouchVisibility() {
if (mReportRejectedTouch == null) {
return;
@@ -1843,7 +1810,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onStatusBarTrackpadEvent(MotionEvent event) {
- mCentralSurfacesComponent.getNotificationPanelViewController().handleExternalTouch(event);
+ mShadeSurface.handleExternalTouch(event);
}
private void onExpandedInvisible() {
@@ -2210,10 +2177,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
*/
@Override
public void setLockscreenUser(int newUserId) {
- if (mLockscreenWallpaper != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- mLockscreenWallpaper.setCurrentUser(newUserId);
- }
- mScrimController.setCurrentUser(newUserId);
if (mWallpaperSupported) {
mWallpaperChangedReceiver.onReceive(mContext, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 4d716c206908..680f19a79a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -45,7 +45,7 @@ import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
index 5c357d7cd3ab..686efb7f1553 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -21,7 +21,7 @@ import android.view.View
import com.android.internal.logging.nano.MetricsProto.MetricsEvent
import com.android.systemui.log.dagger.LSShadeTransitionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index c07b5e062d70..b2c39f7e289f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -40,12 +40,17 @@ import androidx.annotation.NonNull;
import com.android.internal.util.IndentingPrintWriter;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.user.data.model.SelectedUserModel;
+import com.android.systemui.user.data.model.SelectionStatus;
+import com.android.systemui.user.data.repository.UserRepository;
+import com.android.systemui.util.kotlin.JavaAdapter;
import libcore.io.IoUtils;
@@ -59,7 +64,7 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
- Dumpable {
+ Dumpable, CoreStartable {
private static final String TAG = "LockscreenWallpaper";
@@ -72,6 +77,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
private final WallpaperManager mWallpaperManager;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mH;
+ private final JavaAdapter mJavaAdapter;
+ private final UserRepository mUserRepository;
private boolean mCached;
private Bitmap mCache;
@@ -88,6 +95,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
DumpManager dumpManager,
NotificationMediaManager mediaManager,
@Main Handler mainHandler,
+ JavaAdapter javaAdapter,
+ UserRepository userRepository,
UserTracker userTracker) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
mWallpaperManager = wallpaperManager;
@@ -95,6 +104,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
mUpdateMonitor = keyguardUpdateMonitor;
mMediaManager = mediaManager;
mH = mainHandler;
+ mJavaAdapter = javaAdapter;
+ mUserRepository = userRepository;
if (iWallpaperManager != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
// Service is disabled on some devices like Automotive
@@ -106,6 +117,14 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
}
}
+ @Override
+ public void start() {
+ if (!isLockscreenLiveWallpaperEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mUserRepository.getSelectedUser(), this::setSelectedUser);
+ }
+ }
+
public Bitmap getBitmap() {
assertLockscreenLiveWallpaperNotEnabled();
@@ -169,9 +188,15 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
}
}
- public void setCurrentUser(int user) {
+ private void setSelectedUser(SelectedUserModel selectedUserModel) {
assertLockscreenLiveWallpaperNotEnabled();
+ if (selectedUserModel.getSelectionStatus().equals(SelectionStatus.SELECTION_IN_PROGRESS)) {
+ // Wait until the selection has finished before updating.
+ return;
+ }
+
+ int user = selectedUserModel.getUserInfo().id;
if (user != mCurrentUserId) {
if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
mCached = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 47c4023ca8aa..c16e13cd4af3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1476,10 +1476,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- public void setCurrentUser(int currentUser) {
- // Don't care in the base class.
- }
-
private void updateThemeColors() {
if (mScrimBehind == null) return;
int background = Utils.getColorAttr(mScrimBehind.getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
index 12f023b21701..d07378e86ced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
@@ -19,10 +19,10 @@ package com.android.systemui.statusbar.phone
import android.app.PendingIntent
import com.android.systemui.log.dagger.NotifInteractionLog
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.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index c3322808b2b8..604b1f5008db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -64,6 +64,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
private boolean mNeedsUnderflow;
// Individual StatusBarIconViews draw their etc dots centered in this width
private int mIconDotFrameWidth;
+ private boolean mQsExpansionTransitioning;
private boolean mShouldRestrictIcons = true;
// Used to count which states want to be visible during layout
private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
@@ -87,6 +88,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
super.onFinishInflate();
}
+ public void setQsExpansionTransitioning(boolean expansionTransitioning) {
+ mQsExpansionTransitioning = expansionTransitioning;
+ }
+
public void setShouldRestrictIcons(boolean should) {
mShouldRestrictIcons = should;
}
@@ -386,6 +391,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
StatusIconState vs = getViewStateFromChild(child);
if (vs != null) {
vs.applyToView(child);
+ vs.qsExpansionTransitioning = mQsExpansionTransitioning;
}
}
}
@@ -420,6 +426,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
/// StatusBarIconView.STATE_*
public int visibleState = STATE_ICON;
public boolean justAdded = true;
+ public boolean qsExpansionTransitioning = false;
// How far we are from the end of the view actually is the most relevant for animation
float distanceToViewEnd = -1;
@@ -462,12 +469,13 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout {
}
icon.setVisibleState(visibleState, animateVisibility);
- if (animationProperties != null) {
+ if (animationProperties != null && !qsExpansionTransitioning) {
animateTo(view, animationProperties);
} else {
super.applyToView(view);
}
+ qsExpansionTransitioning = false;
justAdded = false;
distanceToViewEnd = currentDistanceToEnd;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index c618be843832..4ae460a3f0e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -21,10 +21,8 @@ import static com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.ST
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.scene.ui.view.WindowRootView;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -89,14 +87,6 @@ public interface CentralSurfacesComponent {
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
- * Creates a NotificationPanelViewController.
- */
- NotificationPanelViewController getNotificationPanelViewController();
-
- /** Creates a QuickSettingsController. */
- QuickSettingsController getQuickSettingsController();
-
- /**
* Creates a StatusBarHeadsUpChangeListener.
*/
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 6b0746f08df0..ebdde78e4ea4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -16,28 +16,20 @@
package com.android.systemui.statusbar.phone.dagger;
-import android.view.LayoutInflater;
-
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelView;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
-import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
@@ -49,10 +41,8 @@ import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
import java.util.concurrent.Executor;
@@ -70,17 +60,6 @@ public abstract class StatusBarViewModule {
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
- /** */
- @Binds
- @CentralSurfacesComponent.CentralSurfacesScope
- abstract ShadeViewController bindsShadeViewController(
- NotificationPanelViewController notificationPanelViewController);
-
- @Binds
- @IntoSet
- abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
- SystemBarAttributesListener systemBarAttributesListener);
-
/**
* Creates a new {@link CollapsedStatusBarFragment}.
*
@@ -145,17 +124,4 @@ public abstract class StatusBarViewModule {
statusBarWindowStateController,
keyguardUpdateMonitor);
}
-
- /**
- * Constructs a new, unattached {@link KeyguardBottomAreaView}.
- *
- * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
- */
- @Provides
- public static KeyguardBottomAreaView providesKeyguardBottomAreaView(
- NotificationPanelView npv, LayoutInflater layoutInflater) {
- return (KeyguardBottomAreaView) layoutInflater.inflate(R
- .layout.keyguard_bottom_area, npv, false);
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 0d057f349dd3..2efdf8a0b181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -14,9 +14,6 @@
package com.android.systemui.statusbar.phone.fragment;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
-
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Fragment;
@@ -39,6 +36,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.animation.Animator;
import com.android.app.animation.Interpolators;
+import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -77,6 +75,8 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -99,12 +99,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private static final String EXTRA_PANEL_STATE = "panel_state";
public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager";
public static final int FADE_IN_DURATION = 320;
+ public static final int FADE_OUT_DURATION = 160;
public static final int FADE_IN_DELAY = 50;
+ private static final int SOURCE_SYSTEM_EVENT_ANIMATOR = 1;
+ private static final int SOURCE_OTHER = 2;
private StatusBarFragmentComponent mStatusBarFragmentComponent;
private PhoneStatusBarView mStatusBar;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
private final ShadeViewController mShadeViewController;
+ private MultiSourceMinAlphaController mEndSideAlphaController;
private LinearLayout mEndSideContent;
private View mClockView;
private View mOngoingCallChip;
@@ -149,7 +153,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
};
private OperatorNameViewController mOperatorNameViewController;
- private StatusBarSystemEventAnimator mSystemEventAnimator;
+ private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;
private final CarrierConfigChangedListener mCarrierConfigCallback =
new CarrierConfigChangedListener() {
@@ -297,14 +301,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
+ mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
mClockView = mStatusBar.findViewById(R.id.clock);
mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
showEndSideContent(false);
showClock(false);
initOperatorName();
initNotificationIconArea();
- mSystemEventAnimator =
- new StatusBarSystemEventAnimator(mEndSideContent, getResources());
+ mSystemEventAnimator = getSystemEventAnimator();
mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
@@ -593,18 +597,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void hideEndSideContent(boolean animate) {
- animateHide(mEndSideContent, animate);
+ if (!animate) {
+ mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER);
+ } else {
+ mEndSideAlphaController.animateToAlpha(/*alpha*/ 0f, SOURCE_OTHER, FADE_OUT_DURATION,
+ InterpolatorsAndroidX.ALPHA_OUT, /*startDelay*/ 0);
+ }
}
private void showEndSideContent(boolean animate) {
- // Only show the system icon area if we are not currently animating
- int state = mAnimationScheduler.getAnimationState();
- if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
- animateShow(mEndSideContent, animate);
+ if (!animate) {
+ mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER);
+ return;
+ }
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER,
+ mKeyguardStateController.getKeyguardFadingAwayDuration(),
+ InterpolatorsAndroidX.LINEAR_OUT_SLOW_IN,
+ mKeyguardStateController.getKeyguardFadingAwayDelay());
} else {
- // We are in the middle of a system status event animation, which will animate the
- // alpha (but not the visibility). Allow the view to become visible again
- mEndSideContent.setVisibility(View.VISIBLE);
+ mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER, FADE_IN_DURATION,
+ InterpolatorsAndroidX.ALPHA_IN, FADE_IN_DELAY);
}
}
@@ -671,7 +684,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
v.animate()
.alpha(0f)
- .setDuration(160)
+ .setDuration(FADE_OUT_DURATION)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(() -> v.setVisibility(state));
@@ -754,6 +767,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
}
+ private StatusBarSystemEventDefaultAnimator getSystemEventAnimator() {
+ return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
+ mEndSideAlphaController.setAlpha(alpha, SOURCE_SYSTEM_EVENT_ANIMATOR);
+ return Unit.INSTANCE;
+ }, (translationX) -> {
+ mEndSideContent.setTranslationX(translationX);
+ return Unit.INSTANCE;
+ }, /*isAnimationRunning*/ false);
+ }
+
private void updateStatusBarLocation(int left, int right) {
int leftMargin = left - mStatusBar.getLeft();
int rightMargin = mStatusBar.getRight() - right;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index f4ab408cc275..7cdb9c0a7aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone.fragment
import com.android.systemui.log.dagger.CollapsedSbFragmentLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
new file mode 100644
index 000000000000..c8836e4235dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.fragment
+
+import android.view.View
+import androidx.core.animation.Interpolator
+import androidx.core.animation.ValueAnimator
+import com.android.app.animation.InterpolatorsAndroidX
+
+/**
+ * A controller that keeps track of multiple sources applying alpha value changes to a view. It will
+ * always apply the minimum alpha value of all sources.
+ */
+internal class MultiSourceMinAlphaController
+@JvmOverloads
+constructor(private val view: View, private val initialAlpha: Float = 1f) {
+
+ private val alphas = mutableMapOf<Int, Float>()
+ private val animators = mutableMapOf<Int, ValueAnimator>()
+
+ /**
+ * Sets the alpha of the provided source and applies it to the view (if no other source has set
+ * a lower alpha currently). If an animator of the same source is still running (i.e.
+ * [animateToAlpha] was called before), that animator is cancelled.
+ */
+ fun setAlpha(alpha: Float, sourceId: Int) {
+ animators[sourceId]?.cancel()
+ updateAlpha(alpha, sourceId)
+ }
+
+ /** Animates to the alpha of the provided source. */
+ fun animateToAlpha(
+ alpha: Float,
+ sourceId: Int,
+ duration: Long,
+ interpolator: Interpolator = InterpolatorsAndroidX.ALPHA_IN,
+ startDelay: Long = 0
+ ) {
+ animators[sourceId]?.cancel()
+ val animator = ValueAnimator.ofFloat(getMinAlpha(), alpha)
+ animator.duration = duration
+ animator.startDelay = startDelay
+ animator.interpolator = interpolator
+ animator.addUpdateListener { updateAlpha(animator.animatedValue as Float, sourceId) }
+ animator.start()
+ animators[sourceId] = animator
+ }
+
+ fun reset() {
+ alphas.clear()
+ animators.forEach { it.value.cancel() }
+ animators.clear()
+ applyAlphaToView()
+ }
+
+ private fun updateAlpha(alpha: Float, sourceId: Int) {
+ alphas[sourceId] = alpha
+ applyAlphaToView()
+ }
+
+ private fun applyAlphaToView() {
+ val minAlpha = getMinAlpha()
+ view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE
+ view.alpha = minAlpha
+ }
+
+ private fun getMinAlpha() = alphas.minOfOrNull { it.value } ?: initialAlpha
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 8f9f0196abfa..eb4d963e8525 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -27,20 +27,23 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
+import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.Multibinds;
+
import java.util.Optional;
import java.util.Set;
import javax.inject.Named;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
-
/** Dagger module for {@link StatusBarFragmentComponent}. */
@Module
public interface StatusBarFragmentModule {
@@ -152,4 +155,10 @@ public interface StatusBarFragmentModule {
/** */
@Multibinds
Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
+
+ /** */
+ @Binds
+ @IntoSet
+ StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
+ SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index b3a1c4075d87..051e88f9264d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -23,7 +23,7 @@ import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.MobileInputLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 74352d29cd9b..54948a4a41c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -250,15 +250,8 @@ constructor(
.distinctUntilChanged()
.onEach { logger.logDefaultMobileIconGroup(it) }
- override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
- if (!isValidSubId(subId)) {
- throw IllegalArgumentException(
- "subscriptionId $subId is not in the list of valid subscriptions"
- )
- }
-
- return getOrCreateRepoForSubId(subId)
- }
+ override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
+ getOrCreateRepoForSubId(subId)
private fun getOrCreateRepoForSubId(subId: Int) =
subIdRepositoryCache[subId]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
index 7e0c145696c9..cea6654a48de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -21,7 +21,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
index 507549b1e234..f4c572308e59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -20,7 +20,7 @@ import android.view.View
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
index cac0ae3dbab4..8a4d14e69652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
@@ -20,7 +20,7 @@ import android.net.Network
import android.net.NetworkCapabilities
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.SharedConnectivityInputLog
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
index 328d901b541d..4b9de85915ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared
import android.net.Network
import android.net.NetworkCapabilities
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** Helper object for logs that are shared between wifi and mobile. */
object LoggerHelper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
index 058eda4400df..9a504c9534f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.shared.data.model
import android.net.NetworkCapabilities
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogMessage
/**
* A model for all of the current default connections(s).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index a4fbc2c93647..a57be665f105 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -96,7 +96,7 @@ constructor(
networkId = DEMO_NET_ID,
isValidated = validated ?: true,
level = level ?: 0,
- ssid = ssid,
+ ssid = ssid ?: DEMO_NET_SSID,
// These fields below aren't supported in demo mode, since they aren't needed to satisfy
// the interface.
@@ -115,5 +115,6 @@ constructor(
companion object {
private const val DEMO_NET_ID = 1234
+ private const val DEMO_NET_SSID = "Demo SSID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
index 4a9ceacb0bd1..f244376f5a5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -20,7 +20,7 @@ import android.net.Network
import android.net.NetworkCapabilities
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.WifiInputLog
import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
index 6ba2a81b4b13..096ad1f45841 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
@@ -22,7 +22,7 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
import com.android.internal.R
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.log.dagger.DeviceStateAutoRotationLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 8cf9493586bc..ef07eed467dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -18,8 +18,8 @@ package com.android.systemui.statusbar.policy
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index a82646aba825..710588c82a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.hardware.biometrics.BiometricSourceType.FACE;
+
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -199,6 +201,11 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
Trace.endSection();
}
+ private void notifyKeyguardFaceAuthEnabledChanged() {
+ // Copy the list to allow removal during callback.
+ new ArrayList<>(mCallbacks).forEach(Callback::onFaceAuthEnabledChanged);
+ }
+
private void notifyUnlockedChanged() {
Trace.beginSection("KeyguardStateController#notifyUnlockedChanged");
// Copy the list to allow removal during callback.
@@ -419,6 +426,16 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
+ public void onBiometricEnrollmentStateChanged(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE) {
+ // We only care about enrollment state here. Keyguard face auth enabled is just
+ // same as face auth enrolled
+ update(false);
+ notifyKeyguardFaceAuthEnabledChanged();
+ }
+ }
+
+ @Override
public void onStartedWakingUp() {
update(false /* updateAlways */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index 21d03386b9e2..cac5e3290a26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -22,6 +22,13 @@ import android.app.PendingIntent
import android.app.RemoteInput
import android.content.Context
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
@@ -48,7 +55,13 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedA
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType
import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
+import java.util.concurrent.FutureTask
+import java.util.concurrent.SynchronousQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
+import kotlin.system.measureTimeMillis
+
/** Returns whether we should show the smart reply view and its smart suggestions. */
fun shouldShowSmartReplyView(
@@ -281,6 +294,51 @@ interface SmartActionInflater {
): Button
}
+private const val ICON_TASK_TIMEOUT_MS = 500L
+private val iconTaskThreadPool = ThreadPoolExecutor(0, 25, 1, TimeUnit.MINUTES, SynchronousQueue())
+
+private fun loadIconDrawableWithTimeout(
+ icon: Icon,
+ packageContext: Context,
+ targetSize: Int,
+): Drawable? {
+ if (icon.type != Icon.TYPE_URI && icon.type != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ return icon.loadDrawable(packageContext)
+ }
+ val bitmapTask = FutureTask {
+ val bitmap: Bitmap?
+ val durationMillis = measureTimeMillis {
+ val source = ImageDecoder.createSource(packageContext.contentResolver, icon.uri)
+ bitmap = ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
+ decoder.setTargetSize(targetSize, targetSize)
+ decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT
+ }
+ }
+ if (durationMillis > ICON_TASK_TIMEOUT_MS) {
+ Log.w(TAG, "Loading $icon took ${durationMillis / 1000f} sec")
+ }
+ checkNotNull(bitmap) { "ImageDecoder.decodeBitmap() returned null" }
+ }
+ val bitmap = runCatching {
+ iconTaskThreadPool.execute(bitmapTask)
+ bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }.getOrElse { ex ->
+ Log.e(TAG, "Failed to load $icon: $ex")
+ bitmapTask.cancel(true)
+ return null
+ }
+ // TODO(b/288561520): rewrite Icon so that we don't need to duplicate this logic
+ val bitmapDrawable = BitmapDrawable(packageContext.resources, bitmap)
+ val result = if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP)
+ AdaptiveIconDrawable(null, bitmapDrawable) else bitmapDrawable
+ if (icon.hasTint()) {
+ result.mutate()
+ result.setTintList(icon.tintList)
+ result.setTintBlendMode(icon.tintBlendMode)
+ }
+ return result
+}
+
/* internal */ class SmartActionInflaterImpl @Inject constructor(
private val constants: SmartReplyConstants,
private val activityStarter: ActivityStarter,
@@ -304,12 +362,12 @@ interface SmartActionInflater {
// We received the Icon from the application - so use the Context of the application to
// reference icon resources.
- val iconDrawable = action.getIcon().loadDrawable(packageContext)
- .apply {
- val newIconSize: Int = context.resources.getDimensionPixelSize(
- R.dimen.smart_action_button_icon_size)
- setBounds(0, 0, newIconSize, newIconSize)
- }
+ val newIconSize = context.resources
+ .getDimensionPixelSize(R.dimen.smart_action_button_icon_size)
+ val iconDrawable =
+ loadIconDrawableWithTimeout(action.getIcon(), packageContext, newIconSize)
+ ?: GradientDrawable()
+ iconDrawable.setBounds(0, 0, newIconSize, newIconSize)
// Add the action icon to the Smart Action button.
setCompoundDrawablesRelative(iconDrawable, null, null, null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index b135d0d8c9dc..1c3a8850df8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
@@ -122,7 +123,12 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {
userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
- updateZenModeConfig();
+ try {
+ Trace.beginSection("updateZenModeConfig");
+ updateZenModeConfig();
+ } finally {
+ Trace.endSection();
+ }
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index b1b8341d9584..d35d34063d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -27,11 +27,12 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.KeyboardShortcuts;
import dagger.Lazy;
+import javax.inject.Inject;
+
/**
* Status bar implementation for "large screen" products that mostly present no on-screen nav.
* Serves as a collection of UI components, rather than showing its own UI.
@@ -78,4 +79,9 @@ public class TvStatusBar implements CoreStartable, CommandQueue.Callbacks {
new Intent(ACTION_SHOW_PIP_MENU).setPackage(mContext.getPackageName()),
SYSTEMUI_PERMISSION);
}
+
+ @Override
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
+ KeyboardShortcuts.show(mContext, deviceId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bcf3b0cbfc86..4a9921eb2d83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -240,8 +240,10 @@ public class StatusBarWindowController {
Insets.of(0, safeTouchRegionHeight, 0, 0));
}
lp.providedInsets = new InsetsFrameProvider[] {
- new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()),
- new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
gestureInsetsProvider
};
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index 066ac04c2727..a9d202911e93 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay
import android.view.View
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** A logger for temporary view changes -- see [TemporaryViewDisplayController]. */
open class TemporaryViewLogger<T : TemporaryViewInfo>(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
index d55751b9d8a0..6706873ebeb4 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.temporarydisplay.TemporaryViewLogger
import com.android.systemui.temporarydisplay.dagger.ChipbarLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
index dfe748afbd41..c109eb4134bb 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
@@ -18,9 +18,9 @@ package com.android.systemui.toast
import com.android.systemui.log.dagger.ToastLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogMessage
import javax.inject.Inject
private const val TAG = "ToastLog"
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index d9a8e0cfb53a..3f31ff94fee7 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -48,6 +48,7 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.KeyboardShortcutsModule;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -96,6 +97,7 @@ import javax.inject.Named;
ReferenceScreenshotModule.class,
StatusBarEventsModule.class,
VolumeModule.class,
+ KeyboardShortcutsModule.class
}
)
public abstract class TvSystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
new file mode 100644
index 000000000000..cefd466374c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user.data.model
+
+import android.content.pm.UserInfo
+
+/** A model for the currently selected user. */
+data class SelectedUserModel(
+ /** Information about the user. */
+ val userInfo: UserInfo,
+ /** The current status of the selection. */
+ val selectionStatus: SelectionStatus,
+)
+
+/** The current status of the selection. */
+enum class SelectionStatus {
+ /** This user has started being selected but the selection hasn't completed. */
+ SELECTION_IN_PROGRESS,
+ /** The selection of this user has completed. */
+ SELECTION_COMPLETE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 3de75ca2ed87..954765c4581d 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -33,6 +33,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -69,6 +71,9 @@ interface UserRepository {
/** List of all users on the device. */
val userInfos: Flow<List<UserInfo>>
+ /** Information about the currently-selected user, including [UserInfo] and other details. */
+ val selectedUser: StateFlow<SelectedUserModel>
+
/** [UserInfo] of the currently-selected user. */
val selectedUserInfo: Flow<UserInfo>
@@ -146,9 +151,6 @@ constructor(
private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
-
override var mainUserId: Int = UserHandle.USER_NULL
private set
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
@@ -174,12 +176,57 @@ constructor(
override var isRefreshUsersPaused: Boolean = false
init {
- observeSelectedUser()
if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
observeUserSwitching()
}
}
+ override val selectedUser: StateFlow<SelectedUserModel> = run {
+ // Some callbacks don't modify the selection status, so maintain the current value.
+ var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
+ conflatedCallbackFlow {
+ fun send(selectionStatus: SelectionStatus) {
+ currentSelectionStatus = selectionStatus
+ trySendWithFailureLogging(
+ SelectedUserModel(tracker.userInfo, selectionStatus),
+ TAG,
+ )
+ }
+
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanging(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_COMPLETE)
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send(currentSelectionStatus)
+ }
+ }
+
+ tracker.addCallback(callback, mainDispatcher.asExecutor())
+ send(currentSelectionStatus)
+
+ awaitClose { tracker.removeCallback(callback) }
+ }
+ .onEach {
+ if (!it.userInfo.isGuest) {
+ lastSelectedNonGuestUserId = it.userInfo.id
+ }
+ }
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus)
+ )
+ }
+
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
+
override fun refreshUsers() {
applicationScope.launch {
val result = withContext(backgroundDispatcher) { manager.aliveUsers }
@@ -201,7 +248,7 @@ constructor(
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -234,38 +281,6 @@ constructor(
.launchIn(applicationScope)
}
- private fun observeSelectedUser() {
- conflatedCallbackFlow {
- fun send() {
- trySendWithFailureLogging(tracker.userInfo, TAG)
- }
-
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanging(newUser: Int, userContext: Context) {
- send()
- }
-
- override fun onProfilesChanged(profiles: List<UserInfo>) {
- send()
- }
- }
-
- tracker.addCallback(callback, mainDispatcher.asExecutor())
- send()
-
- awaitClose { tracker.removeCallback(callback) }
- }
- .onEach {
- if (!it.isGuest) {
- lastSelectedNonGuestUserId = it.id
- }
-
- _selectedUserInfo.value = it
- }
- .launchIn(applicationScope)
- }
-
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
index 891ee0cf66d7..e32ed5fd3671 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
@@ -16,12 +16,19 @@
package com.android.systemui.util.kotlin
+import android.annotation.UserHandleAware
import android.annotation.WorkerThread
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import com.android.systemui.util.Assert
+/**
+ * Determines whether a component is actually enabled (not just its default value).
+ *
+ * @throws IllegalArgumentException if the component is not found
+ */
@WorkerThread
+@UserHandleAware
fun PackageManager.isComponentActuallyEnabled(componentInfo: ComponentInfo): Boolean {
Assert.isNotMainThread()
return when (getComponentEnabledSetting(componentInfo.componentName)) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
index 09268007dddc..d69b10ffb210 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
@@ -18,7 +18,7 @@ package com.android.systemui.util.wakelock
import android.os.PowerManager
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
class WakeLockLogger @Inject constructor(@WakeLockLog private val buffer: LogBuffer) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7456d349a933..93622200ad46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -476,7 +476,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 4c7e6b007f38..5144d1966222 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -334,10 +334,8 @@ public final class WMShell implements
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis,
+ int backDisposition, boolean showImeSwitcher) {
if (displayId == mDisplayTracker.getDefaultDisplayId()
&& (vis & InputMethodService.IME_VISIBLE) != 0) {
oneHanded.stopOneHanded(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 0dcd404d2fc5..231898884adf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,21 +21,22 @@ import android.view.View
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
-import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceConfig
+import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockTickRate
-import com.android.systemui.log.LogBuffer
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,7 +65,6 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import java.util.TimeZone
import java.util.concurrent.Executor
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@@ -122,7 +122,9 @@ class ClockEventControllerTest : SysuiTestCase() {
bouncerRepository = bouncerRepository,
configurationRepository = FakeConfigurationRepository(),
),
- KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b21cc6dde815..9e561ed290f7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -408,4 +408,18 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners(mView);
}
+
+ @Test
+ public void testSplitShadeEnabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void testSplitShadeDisabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 5a56bafc1992..d3b41902499c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -97,7 +97,21 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
`when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
.thenReturn(deleteButton)
`when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
- constructViewController()
+ pinViewController =
+ KeyguardPinViewController(
+ keyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController,
+ featureFlags
+ )
}
@Test
@@ -121,10 +135,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
`when`(passwordTextView.text).thenReturn("")
- constructViewController()
pinViewController.startAppearAnimation()
-
verify(deleteButton).visibility = View.INVISIBLE
verify(enterButton).visibility = View.INVISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -138,10 +150,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
`when`(passwordTextView.text).thenReturn("")
- constructViewController()
pinViewController.startAppearAnimation()
-
verify(deleteButton).visibility = View.VISIBLE
verify(enterButton).visibility = View.VISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -153,22 +163,4 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
pinViewController.handleAttemptLockout(0)
verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
}
-
- fun constructViewController() {
- pinViewController =
- KeyguardPinViewController(
- keyguardPinView,
- keyguardUpdateMonitor,
- securityMode,
- lockPatternUtils,
- mKeyguardSecurityCallback,
- keyguardMessageAreaControllerFactory,
- mLatencyTracker,
- liftToActivateListener,
- mEmergencyButtonController,
- falsingCollector,
- postureController,
- featureFlags
- )
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index e561f1f233b4..58b1edcc5511 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -62,12 +62,12 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.biometrics.SideFpsUiRequestSource;
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
@@ -395,6 +395,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
// WHEN a request is made from the SimPin screens to show the next security method
when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true);
mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
/* authenticated= */true,
TARGET_USER_ID,
@@ -423,6 +424,28 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
}
@Test
+ public void showNextSecurityScreenOrFinish_SimPin_Swipe() {
+ // GIVEN the current security method is SimPin
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+ // WHEN security method is SWIPE
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false);
+ mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ // THEN the next security method of None will dismiss keyguard.
+ verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
+ }
+
+
+ @Test
public void onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() {
KeyguardSecurityContainer.SwipeListener registeredSwipeListener =
getRegisteredSwipeListener();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index a2c632936047..512e5dc1a0d6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -155,4 +156,18 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true);
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true);
}
+
+ @Test
+ public void splitShadeEnabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void splitShadeDisabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 901c3fb05b2f..5abab6239b1e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -83,6 +83,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -3000,6 +3001,34 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
TelephonyManager.SIM_STATE_UNKNOWN);
}
+ @Test
+ public void testOnSimStateChanged_HandleSimStateNotReady() {
+ KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy(
+ KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_NOT_READY);
+ verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0,
+ TelephonyManager.SIM_STATE_NOT_READY);
+ }
+
+ @Test
+ public void onAuthEnrollmentChangesCallbacksAreNotified() {
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ ArgumentCaptor<AuthController.Callback> authCallback = ArgumentCaptor.forClass(
+ AuthController.Callback.class);
+ verify(mAuthController).addCallback(authCallback.capture());
+
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ authCallback.getValue().onEnrollmentsChanged(TYPE_FINGERPRINT);
+ mTestableLooper.processAllMessages();
+ verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FINGERPRINT);
+
+ authCallback.getValue().onEnrollmentsChanged(BiometricAuthenticator.TYPE_FACE);
+ mTestableLooper.processAllMessages();
+ verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index c88c4d65f412..e7e1cc94b7c8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -19,6 +19,7 @@ package com.android.keyguard;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
+import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -143,6 +144,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
+ mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mUnderTest = new LockIconViewController(
mLockIconView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 79c87cfd1f3e..796e66514afe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -96,6 +96,7 @@ import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -139,6 +140,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
@Mock
private Display mDisplay;
@Mock
+ private CommandRegistry mCommandRegistry;
+ @Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@@ -231,8 +234,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mExecutor,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer"))));
- mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
- mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory,
+ mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
+ mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
+ mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mAuthController) {
@@ -1226,8 +1230,9 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mFaceScanningProviders.add(mFaceScanningDecorProvider);
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true);
- ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor,
- mSecureSettings, mUserTracker, mDisplayTracker, mDotViewController,
+ ScreenDecorations screenDecorations = new ScreenDecorations(mContext,
+ mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
+ mDotViewController,
mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
screenDecorations.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index a63652c27a6c..f0425e923f26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -53,6 +53,7 @@ import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -65,6 +66,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+// TODO(b/288926821): un-ignore once fixed
+@Ignore("Needs deeper investigation/refactoring, see b/289212459 and b/289392705")
@LargeTest
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 224875590d75..0e0d0e3f3748 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -43,11 +43,12 @@ import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -58,6 +59,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -70,6 +72,7 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
private const val REQUEST_ID = 2L
@@ -80,6 +83,7 @@ private const val DISPLAY_HEIGHT = 1920
private const val SENSOR_WIDTH = 30
private const val SENSOR_HEIGHT = 60
+@ExperimentalCoroutinesApi
@SmallTest
@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@@ -116,6 +120,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var udfpsUtils: UdfpsUtils
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
UdfpsKeyguardAccessibilityDelegate
+ @Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -148,6 +153,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
+ udfpsKeyguardViewModels,
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 30e54474bbde..58982d13481d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -59,6 +60,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.VibrationAttributes;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -83,13 +85,14 @@ import com.android.systemui.biometrics.udfps.InteractionEvent;
import com.android.systemui.biometrics.udfps.NormalizedTouchData;
import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
import com.android.systemui.biometrics.udfps.TouchProcessorResult;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -219,6 +222,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
@Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ @Mock
+ private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
// Capture listeners so that they can be used to send events
@Captor
@@ -318,7 +323,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
- mUdfpsKeyguardAccessibilityDelegate);
+ mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -1200,8 +1205,53 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
+ public void fingerDown_falsingManagerInformed() throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // THEN falsing manager is informed of the touch
+ verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
+ }
+
+ @Test
public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath()
throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_UP is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.second);
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the new FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
+
+ private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent()
+ throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
@@ -1227,27 +1277,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUp);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the new FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ return new Pair<>(processorResultDown, processorResultUp);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 263ce1a2e9f5..8f8004f1cbb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -7,7 +7,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -54,10 +54,11 @@ class LogContextInteractorImplTest : SysuiTestCase() {
LogContextInteractorImpl(
testScope.backgroundScope,
foldProvider,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ repository = keyguardTransitionRepository,
+ scope = testScope.backgroundScope,
+ )
+ .keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
index 69547105d419..9260f63c1195 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.controller
import android.content.Context
+import android.content.pm.PackageManager
import android.os.Handler
import android.os.UserHandle
import android.testing.AndroidTestingRunner
@@ -34,6 +35,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@@ -42,12 +44,14 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
@Mock private lateinit var context: Context
@Mock private lateinit var bgHandler: Handler
+ @Mock private lateinit var packageManager: PackageManager
private lateinit var underTest: PackageUpdateMonitor
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ whenever(context.packageManager).thenReturn(packageManager)
}
@Test
@@ -58,9 +62,16 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
// There are two receivers registered
verify(context, times(2))
.registerReceiverAsUser(any(), eq(USER), any(), eq(null), eq(bgHandler))
+ verify(packageManager).registerPackageMonitorCallback(any(), eq(USER.getIdentifier()))
+ // context will be used to get PackageManager, the test should clear invocations
+ // for next startMonitoring() assertion
+ clearInvocations(context)
underTest.startMonitoring()
+ // No more interactions for registerReceiverAsUser
verifyNoMoreInteractions(context)
+ // No more interactions for registerPackageMonitorCallback
+ verifyNoMoreInteractions(packageManager)
}
@Test
@@ -69,12 +80,20 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
underTest.startMonitoring()
clearInvocations(context)
+ clearInvocations(packageManager)
underTest.stopMonitoring()
verify(context).unregisterReceiver(any())
+ verify(packageManager).unregisterPackageMonitorCallback(any())
+ // context will be used to get PackageManager, the test should clear invocations
+ // for next stopMonitoring() assertion
+ clearInvocations(context)
underTest.stopMonitoring()
+ // No more interactions for unregisterReceiver
verifyNoMoreInteractions(context)
+ // No more interactions for unregisterPackageMonitorCallback
+ verifyNoMoreInteractions(packageManager)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
index bd029a727ee3..a341ca365ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -17,7 +17,7 @@
package com.android.systemui.dump
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.LogcatEchoTracker
/**
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 88d527c17c9d..ddcbe7993697 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -217,6 +217,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot);
when(mDreamingToLockscreenTransitionViewModel.getDreamOverlayAlpha())
.thenReturn(mock(Flow.class));
+ when(mDreamingToLockscreenTransitionViewModel.getTransitionEnded())
+ .thenReturn(mock(Flow.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mViewMediator, mKeyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b4bd473b8b8c..925ac30b99fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -10,7 +10,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -67,10 +67,11 @@ class ResourceTrimmerTest : SysuiTestCase() {
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
globalWindowManager,
testScope.backgroundScope,
testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 5922cbf6268e..d62db5d1b890 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -49,7 +49,7 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
@@ -216,7 +216,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
)
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
@@ -260,6 +264,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
assertThat(authenticated()).isTrue()
assertThat(authRunning()).isFalse()
+ assertThat(canFaceAuthRun()).isFalse()
}
private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
@@ -417,13 +422,9 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
FACE_ERROR_CANCELED,
"First auth attempt cancellation completed"
)
- assertThat(authStatus())
- .isEqualTo(
- ErrorAuthenticationStatus(
- FACE_ERROR_CANCELED,
- "First auth attempt cancellation completed"
- )
- )
+ val value = authStatus() as ErrorAuthenticationStatus
+ assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
+ assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")
faceAuthenticateIsCalled()
uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
@@ -551,20 +552,6 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
- fun authenticateDoesNotRunWhenDeviceIsSleeping() =
- testScope.runTest {
- testGatingCheckForFaceAuth {
- keyguardRepository.setWakefulnessModel(
- WakefulnessModel(
- state = WakefulnessState.ASLEEP,
- lastWakeReason = WakeSleepReason.OTHER,
- lastSleepReason = WakeSleepReason.OTHER,
- )
- )
- }
- }
-
- @Test
fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -586,6 +573,29 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
+ fun authenticateRunsWhenSecureCameraIsActiveIfBouncerIsShowing() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+ bouncerRepository.setAlternateVisible(false)
+ bouncerRepository.setPrimaryShow(false)
+
+ assertThat(canFaceAuthRun()).isTrue()
+
+ // launch secure camera
+ fakeCommandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+ }
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+ assertThat(canFaceAuthRun()).isFalse()
+
+ // but bouncer is shown after that.
+ bouncerRepository.setPrimaryShow(true)
+ assertThat(canFaceAuthRun()).isTrue()
+ }
+
+ @Test
fun authenticateDoesNotRunOnUnsupportedPosture() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -616,6 +626,27 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
}
@Test
+ fun authenticateFallbacksToDetectionWhenUserIsAlreadyTrustedByTrustManager() =
+ testScope.runTest {
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+ whenever(bypassController.bypassEnabled).thenReturn(true)
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ trustRepository.setCurrentUserTrusted(true)
+ assertThat(canFaceAuthRun()).isFalse()
+ underTest.authenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
+ faceAuthenticateIsNotCalled()
+
+ faceDetectIsCalled()
+ }
+
+ @Test
fun everythingWorksWithFaceAuthRefactorFlagDisabled() =
testScope.runTest {
featureFlags.set(FACE_AUTH_REFACTOR, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 80700e59a2d4..3e81cd336824 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.hardware.biometrics.BiometricFaceConstants
import android.os.Handler
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -30,6 +31,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -39,6 +41,7 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -86,10 +89,15 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest =
SystemUIKeyguardFaceAuthInteractor(
+ mContext,
testScope.backgroundScope,
dispatcher,
faceAuthRepository,
@@ -144,6 +152,22 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
}
@Test
+ fun whenFaceIsLockedOutAnyAttemptsToTriggerFaceAuthMustProvideLockoutError() =
+ testScope.runTest {
+ underTest.start()
+ val authenticationStatus = collectLastValue(underTest.authenticationStatus)
+ faceAuthRepository.setLockedOut(true)
+
+ underTest.onDeviceLifted()
+
+ val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus
+ assertThat(outputValue.msgId)
+ .isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
+ assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
+ @Test
fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index e9c22f9d551d..0050d64d7505 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -292,10 +292,11 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() {
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
repository = keyguardRepository,
logger = logger,
featureFlags =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index fa4941cbb895..9e9c25eafa33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.RoboPilotTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -54,7 +54,10 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- underTest = KeyguardTransitionInteractor(repository, testScope.backgroundScope)
+ underTest = KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ ).keyguardTransitionInteractor
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index b5590154f7f4..d01a46e06b9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.TestScopeProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.flags.FakeFeatureFlags
@@ -92,76 +93,97 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
+ testScope = TestScopeProvider.getTestScope()
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
transitionRepository = spy(FakeKeyguardTransitionRepository())
- transitionInteractor = KeyguardTransitionInteractor(
- transitionRepository, testScope.backgroundScope)
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
- fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- shadeRepository = shadeRepository,
- ).apply { start() }
-
- fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- keyguardSecurityModel = keyguardSecurityModel,
- ).apply { start() }
-
- fromDreamingTransitionInteractor = FromDreamingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
-
- fromAodTransitionInteractor = FromAodTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
-
- fromGoneTransitionInteractor = FromGoneTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
-
- fromDozingTransitionInteractor = FromDozingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
-
- fromOccludedTransitionInteractor = FromOccludedTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
-
- fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ transitionInteractor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope,
+ repository = transitionRepository,
+ )
+ .keyguardTransitionInteractor
+
+ fromLockscreenTransitionInteractor =
+ FromLockscreenTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ shadeRepository = shadeRepository,
+ )
+ .apply { start() }
+
+ fromPrimaryBouncerTransitionInteractor =
+ FromPrimaryBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ keyguardSecurityModel = keyguardSecurityModel,
+ )
+ .apply { start() }
+
+ fromDreamingTransitionInteractor =
+ FromDreamingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromAodTransitionInteractor =
+ FromAodTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromGoneTransitionInteractor =
+ FromGoneTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromDozingTransitionInteractor =
+ FromDozingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromOccludedTransitionInteractor =
+ FromOccludedTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromAlternateBouncerTransitionInteractor =
+ FromAlternateBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 08e99dc6b7d0..6e7ba6dd1ecb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -46,7 +46,11 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
private val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(fakeKeyguardTransitionRepository, TestScope().backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = fakeKeyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
private lateinit var underTest: LightRevealScrimInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
new file mode 100644
index 000000000000..1baca2184e9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsKeyguardInteractorTest : SysuiTestCase() {
+ private val burnInProgress = 1f
+ private val burnInYOffset = 20
+ private val burnInXOffset = 10
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var burnInInteractor: BurnInInteractor
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ private lateinit var underTest: UdfpsKeyguardInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+ burnInInteractor =
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ )
+
+ underTest =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ burnInInteractor,
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun dozeChanges_updatesUdfpsAodModel() =
+ testScope.runTest {
+ val burnInOffsets by collectLastValue(underTest.burnInOffsets)
+ initializeBurnInOffsets()
+
+ // WHEN we're not dozing
+ setAwake()
+ runCurrent()
+
+ // THEN burn in offsets are 0
+ assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
+ assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
+ assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
+
+ // WHEN we're in the middle of the doze amount change
+ keyguardRepository.setDozeAmount(.50f)
+ runCurrent()
+
+ // THEN burn in is updated (between 0 and the full offset)
+ assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
+ assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
+ assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
+ assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
+ assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
+ assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
+
+ // WHEN we're fully dozing
+ keyguardRepository.setDozeAmount(1f)
+ runCurrent()
+
+ // THEN burn in offsets are updated to final current values (for the given time)
+ assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
+ assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
+ assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
+ }
+
+ private fun initializeBurnInOffsets() {
+ whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
+ whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
+ .thenReturn(burnInXOffset)
+ whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(false)))
+ .thenReturn(burnInYOffset)
+ }
+
+ private fun setAwake() {
+ keyguardRepository.setDozeAmount(0f)
+ burnInInteractor.dozeTimeTick()
+
+ bouncerRepository.setAlternateVisible(false)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ bouncerRepository.setPrimaryShow(false)
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.POWER_BUTTON,
+ WakeSleepReason.POWER_BUTTON,
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
new file mode 100644
index 000000000000..2e97208e44de
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.graphics.Point
+import android.view.ViewGroup
+import android.view.WindowManager
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.monet.utils.ArgbSubject.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultLockscreenLayoutTest : SysuiTestCase() {
+ private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ rootView = KeyguardRootView(context, null)
+ defaultLockscreenLayout =
+ DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context)
+ }
+
+ @Test
+ fun testLayoutViews_KeyguardIndicationArea() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.keyguard_indication_area)
+ assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT)
+ assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+
+ @Test
+ fun testLayoutViews_lockIconView() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ }
+
+ @Test
+ fun testCenterLockIcon() {
+ defaultLockscreenLayout.centerLockIcon(Point(5, 6), 1F, 5, rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+
+ assertThat(constraint.layout.mWidth).isEqualTo(2)
+ assertThat(constraint.layout.mHeight).isEqualTo(2)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.topMargin).isEqualTo(5)
+ assertThat(constraint.layout.startMargin).isEqualTo(4)
+ }
+
+ /** Get the ConstraintLayout constraint of the view. */
+ private fun getViewConstraint(viewId: Int): ConstraintSet.Constraint {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(rootView)
+ return constraintSet.getConstraint(viewId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
new file mode 100644
index 000000000000..145b2fdb000f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import java.io.PrintWriter
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener
+ @Mock private lateinit var commandRegistry: CommandRegistry
+ @Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock private lateinit var pw: PrintWriter
+ private lateinit var command: () -> Command
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ keyguardLayoutManagerCommandListener =
+ KeyguardLayoutManagerCommandListener(
+ commandRegistry,
+ keyguardLayoutManager,
+ )
+ keyguardLayoutManagerCommandListener.start()
+ command =
+ withArgCaptor<() -> Command> {
+ verify(commandRegistry).registerCommand(eq("layout"), capture())
+ }
+ }
+
+ @Test
+ fun testHelp() {
+ command().execute(pw, listOf("help"))
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testBlank() {
+ command().execute(pw, listOf())
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testValidArg() {
+ bindFakeIdMapToLayoutManager()
+ command().execute(pw, listOf("fake"))
+ verify(keyguardLayoutManager).transitionToLayout("fake")
+ }
+
+ private fun bindFakeIdMapToLayoutManager() {
+ val map = mapOf("fake" to mock(LockscreenLayout::class.java))
+ whenever(keyguardLayoutManager.layoutIdMap).thenReturn(map)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
new file mode 100644
index 000000000000..95b2030de923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ @Mock lateinit var keyguardRootView: KeyguardRootView
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(defaultLockscreenLayout.id).thenReturn(DEFAULT)
+ keyguardLayoutManager =
+ KeyguardLayoutManager(
+ configurationController,
+ setOf(defaultLockscreenLayout),
+ keyguardRootView
+ )
+ }
+
+ @Test
+ fun testDefaultLayout() {
+ keyguardLayoutManager.transitionToLayout(DEFAULT)
+ verify(defaultLockscreenLayout).layoutViews(keyguardRootView)
+ }
+
+ @Test
+ fun testTransitionToLayout_validId() {
+ assertThat(keyguardLayoutManager.transitionToLayout(DEFAULT)).isTrue()
+ }
+ @Test
+ fun testTransitionToLayout_invalidId() {
+ assertThat(keyguardLayoutManager.transitionToLayout("abc")).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index a3413466d62e..c67f53519957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -22,8 +22,18 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
@@ -47,7 +57,12 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
}
@@ -60,7 +75,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
val job =
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -82,7 +97,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
// Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.5f))
@@ -104,7 +119,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.2f))
@@ -126,7 +141,7 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
val job =
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -138,13 +153,44 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
job.cancel()
}
- private fun step(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
+ @Test
+ fun transitionEnded() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<TransitionStep>()
+
+ val job = underTest.transitionEnded.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.1f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 1.0f, CANCELED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 1.0f, FINISHED))
+
+ assertThat(values.size).isEqualTo(3)
+ values.forEach {
+ assertThat(it.transitionState == FINISHED || it.transitionState == CANCELED)
+ .isTrue()
+ }
+
+ job.cancel()
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ from = DREAMING,
+ to = LOCKSCREEN,
value = value,
transitionState = state,
ownerName = "DreamingToLockscreenTransitionViewModelTest"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 694539b0cbfe..75c8bff326b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = GoneToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 29886d5481b9..d02b3fccef1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -41,13 +41,12 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -210,10 +209,10 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
repository = repository,
logger = UiEventLoggerFake(),
featureFlags = featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index ea17751782c4..12fe07f0827e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index bf56a981fa31..83ae631ed164 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToOccludedTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 34da26ecc0bf..88603999cdd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = OccludedToLockscreenTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index f88b71d469cf..d8c78ebdca49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -55,7 +55,12 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
new file mode 100644
index 000000000000..436c09ca4a05
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsAodViewModelTest : SysuiTestCase() {
+ private val defaultPadding = 12
+ private lateinit var underTest: UdfpsAodViewModel
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+
+ val udfpsKeyguardInteractor =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+
+ underTest =
+ UdfpsAodViewModel(
+ udfpsKeyguardInteractor,
+ context,
+ )
+ }
+
+ @Test
+ fun alphaAndVisibleUpdates_onDozeAmountChanges() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha)
+ val visible by collectLastValue(underTest.isVisible)
+
+ keyguardRepository.setDozeAmount(0f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ keyguardRepository.setDozeAmount(.65f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(.65f)
+ assertThat(visible).isTrue()
+
+ keyguardRepository.setDozeAmount(.23f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(.23f)
+ assertThat(visible).isTrue()
+
+ keyguardRepository.setDozeAmount(1f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(1f)
+ assertThat(visible).isTrue()
+ }
+
+ @Test
+ fun paddingUpdates_onScaleForResolutionChanges() =
+ testScope.runTest {
+ val padding by collectLastValue(underTest.padding)
+
+ configRepository.setScaleForResolution(1f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding)
+
+ configRepository.setScaleForResolution(2f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding * 2)
+
+ configRepository.setScaleForResolution(.5f)
+ runCurrent()
+ assertThat(padding).isEqualTo((defaultPadding * .5f).toInt())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
new file mode 100644
index 000000000000..a30e2a601e9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+/** Tests UdfpsFingerprintViewModel specific flows. */
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsFingerprintViewModelTest : SysuiTestCase() {
+ private val defaultPadding = 12
+ private lateinit var underTest: FingerprintViewModel
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ transitionRepository = FakeKeyguardTransitionRepository()
+ val transitionInteractor =
+ KeyguardTransitionInteractor(
+ transitionRepository,
+ testScope.backgroundScope,
+ )
+ val udfpsKeyguardInteractor =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+
+ underTest =
+ FingerprintViewModel(
+ context,
+ transitionInteractor,
+ udfpsKeyguardInteractor,
+ )
+ }
+
+ @Test
+ fun paddingUpdates_onScaleForResolutionChanges() =
+ testScope.runTest {
+ val padding by collectLastValue(underTest.padding)
+
+ configRepository.setScaleForResolution(1f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding)
+
+ configRepository.setScaleForResolution(2f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding * 2)
+
+ configRepository.setScaleForResolution(.5f)
+ runCurrent()
+ assertThat(padding).isEqualTo((defaultPadding * .5).toInt())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
new file mode 100644
index 000000000000..d58ceee40c68
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.Utils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+/** Tests UDFPS lockscreen view model transitions. */
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsLockscreenViewModelTest : SysuiTestCase() {
+ private val lockscreenColorResId = android.R.attr.textColorPrimary
+ private val alternateBouncerResId = com.android.internal.R.attr.materialColorOnPrimaryFixed
+ private val lockscreenColor = Utils.getColorAttrDefaultColor(context, lockscreenColorResId)
+ private val alternateBouncerColor =
+ Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
+
+ private lateinit var underTest: UdfpsLockscreenViewModel
+ private lateinit var testScope: TestScope
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ transitionRepository = FakeKeyguardTransitionRepository()
+ val transitionInteractor =
+ KeyguardTransitionInteractor(
+ transitionRepository,
+ testScope.backgroundScope,
+ )
+ underTest =
+ UdfpsLockscreenViewModel(
+ context,
+ lockscreenColorResId,
+ alternateBouncerResId,
+ transitionInteractor,
+ )
+ }
+
+ @Test
+ fun goneToAodTransition() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.RUNNING: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.FINISHED: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun lockscreenToAod() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun aodToLockscreen() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+
+ // TransitionState.RUNNING: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+ }
+
+ @Test
+ fun lockscreenToAlternateBouncer() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+ }
+
+ fun alternateBouncerToPrimaryBouncer() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isFalse()
+ }
+
+ fun alternateBouncerToAod() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun lockscreenToOccluded() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun occludedToLockscreen() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+ }
+}
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 0cf6d3da7e9c..b5eae5b03b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -2,6 +2,7 @@ package com.android.systemui.log
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.core.Logger
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -32,7 +33,8 @@ class LogBufferTest : SysuiTestCase() {
@Test
fun log_shouldSaveLogToBuffer() {
- buffer.log("Test", LogLevel.INFO, "Some test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("Some test message")
val dumpedString = dumpBuffer()
@@ -41,8 +43,9 @@ class LogBufferTest : SysuiTestCase() {
@Test
fun log_shouldRotateIfLogBufferIsFull() {
- buffer.log("Test", LogLevel.INFO, "This should be rotated")
- buffer.log("Test", LogLevel.INFO, "New test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("This should be rotated")
+ logger.i("New test message")
val dumpedString = dumpBuffer()
@@ -53,7 +56,8 @@ class LogBufferTest : SysuiTestCase() {
fun dump_writesExceptionAndStacktrace() {
buffer = createBuffer()
val exception = createTestException("Exception message", "TestClass")
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -72,7 +76,8 @@ class LogBufferTest : SysuiTestCase() {
"TestClass",
cause = createTestException("The real cause!", "TestClass")
)
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -93,7 +98,8 @@ class LogBufferTest : SysuiTestCase() {
)
)
exception.addSuppressed(createTestException("Second suppressed exception", "SecondClass"))
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedStr = dumpBuffer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
new file mode 100644
index 000000000000..ab19b3aeceb0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
@@ -0,0 +1,140 @@
+package com.android.systemui.log.core
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnitRunner
+
+@SmallTest
+@RunWith(MockitoJUnitRunner::class)
+class LoggerTest : SysuiTestCase() {
+ @Mock private lateinit var buffer: MessageBuffer
+ private lateinit var message: LogMessage
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(buffer.obtain(any(), any(), any(), isNull())).thenAnswer {
+ message = LogMessageImpl.Factory.create()
+ return@thenAnswer message
+ }
+ }
+
+ @Test
+ fun log_shouldCommitLogMessage() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) {
+ int1 = 1
+ str1 = "test"
+ bool1 = true
+ }
+
+ assertThat(message.int1).isEqualTo(1)
+ assertThat(message.str1).isEqualTo("test")
+ assertThat(message.bool1).isEqualTo(true)
+ }
+
+ @Test
+ fun log_shouldUseCorrectLoggerTag() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(eq("LoggerTest"), any(), any(), nullable())
+ }
+
+ @Test
+ fun v_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun v_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun d_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun d_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun i_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun i_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun w_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun w_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun e_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun e_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 12f46898ab8d..83182c5cf1b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -18,8 +18,8 @@ package com.android.systemui.log.table
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index b40ebc9bb156..91b0245be8d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -193,6 +193,17 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
}
@Test
+ fun dozeWakeUpAnimationWaiting_inSplitShade_mediaIsHidden() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = true
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
+ }
+
+ @Test
fun dozing_inSingleShade_mediaIsVisible() {
val splitShadeContainer = FrameLayout(context)
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
@@ -203,6 +214,17 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
+ @Test
+ fun dozeWakeUpAnimationWaiting_inSingleShade_mediaIsVisible() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = false
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
+ }
+
private fun setDozing() {
whenever(statusBarStateController.isDozing).thenReturn(true)
statusBarStateListener.onDozingChanged(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 7df54d44e69e..e4f89a226a34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -291,13 +291,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -525,16 +525,16 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);
assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isTrue();
+ assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
index 9e5422470d8b..5890cbd06476 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -97,10 +97,11 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
multiShadeInteractor = interactor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = keyguardTransitionRepository,
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
falsingManager = falsingManager,
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 697d1a3b775c..25d494cee5e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
@@ -363,7 +364,7 @@ public class NavigationBarTest extends SysuiTestCase {
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
defaultNavBar.setImeWindowStatus(
- DEFAULT_DISPLAY, null, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false);
+ DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
| NAVIGATION_HINT_IME_SWITCHER_SHOWN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index a60dad4a14fe..fe6c9b3fe7b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -15,9 +15,11 @@ package com.android.systemui.qs
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
+import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.accessibility.AccessibilityNodeInfo
@@ -55,19 +57,24 @@ class QSPanelTest : SysuiTestCase() {
private lateinit var footer: View
+ private val themedContext = TestableContext(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ )
+
@Before
@Throws(Exception::class)
fun setup() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ // Apply only the values of the theme that are not defined
testableLooper.runWithLooper {
- qsPanel = QSPanel(context, null)
+ qsPanel = QSPanel(themedContext, null)
qsPanel.mUsingMediaPlayer = true
qsPanel.initialize(qsLogger)
// QSPanel inflates a footer inside of it, mocking it here
- footer = LinearLayout(context).apply { id = R.id.qs_footer }
+ footer = LinearLayout(themedContext).apply { id = R.id.qs_footer }
qsPanel.addView(footer, MATCH_PARENT, 100)
qsPanel.onFinishInflate()
// Provides a parent with non-zero size for QSPanel
@@ -105,12 +112,12 @@ class QSPanelTest : SysuiTestCase() {
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(context, QSIconViewImpl(context))
+ QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
)
)
- val mediaView = FrameLayout(context)
- mediaView.addView(View(context), MATCH_PARENT, 800)
+ val mediaView = FrameLayout(themedContext)
+ mediaView.addView(View(themedContext), MATCH_PARENT, 800)
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
@@ -135,12 +142,12 @@ class QSPanelTest : SysuiTestCase() {
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(context, QSIconViewImpl(context))
+ QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
)
)
- val mediaView = FrameLayout(context)
- mediaView.addView(View(context), MATCH_PARENT, 800)
+ val mediaView = FrameLayout(themedContext)
+ mediaView.addView(View(themedContext), MATCH_PARENT, 800)
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
@@ -161,7 +168,10 @@ class QSPanelTest : SysuiTestCase() {
@Test
fun testBottomPadding() {
val padding = 10
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_bottom, padding)
+ themedContext.orCreateTestableResources.addOverride(
+ R.dimen.qs_panel_padding_bottom,
+ padding
+ )
qsPanel.updatePadding()
assertThat(qsPanel.paddingBottom).isEqualTo(padding)
}
@@ -170,8 +180,11 @@ class QSPanelTest : SysuiTestCase() {
fun testTopPadding() {
val padding = 10
val paddingCombined = 100
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, paddingCombined)
+ themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
+ themedContext.orCreateTestableResources.addOverride(
+ R.dimen.qs_panel_padding_top,
+ paddingCombined
+ )
qsPanel.updatePadding()
assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 87892539ccfe..f55ef65a8fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -26,12 +26,14 @@ import static org.mockito.Mockito.mock;
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 android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -56,14 +58,17 @@ public class TileLayoutTest extends SysuiTestCase {
private Resources mResources;
private int mLayoutSizeForOneTile;
private TileLayout mTileLayout; // under test
+ private Context mSpyContext;
+
@Before
public void setUp() throws Exception {
- Context context = Mockito.spy(mContext);
- mResources = Mockito.spy(context.getResources());
- Mockito.when(mContext.getResources()).thenReturn(mResources);
+ mSpyContext = Mockito.spy(
+ new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_QuickSettings));
+ mResources = Mockito.spy(mSpyContext.getResources());
+ when(mSpyContext.getResources()).thenReturn(mResources);
- mTileLayout = new TileLayout(context);
+ mTileLayout = new TileLayout(mSpyContext);
// Layout needs to leave space for the tile margins. Three times the margin size is
// sufficient for any number of columns.
mLayoutSizeForOneTile =
@@ -73,7 +78,7 @@ public class TileLayoutTest extends SysuiTestCase {
private QSPanelControllerBase.TileRecord createTileRecord() {
return new QSPanelControllerBase.TileRecord(
mock(QSTile.class),
- spy(new QSTileViewImpl(mContext, new QSIconViewImpl(mContext))));
+ spy(new QSTileViewImpl(mSpyContext, new QSIconViewImpl(mSpyContext))));
}
@Test
@@ -161,7 +166,7 @@ public class TileLayoutTest extends SysuiTestCase {
.layout(left2.capture(), top2.capture(), right2.capture(), bottom2.capture());
// We assume two tiles will always fit side-by-side.
- assertTrue(mContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1);
+ assertTrue(mSpyContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1);
// left <= right, top <= bottom
assertTrue(left1.getValue() <= right1.getValue());
@@ -218,16 +223,16 @@ public class TileLayoutTest extends SysuiTestCase {
@Test
public void resourcesChanged_updateResources_returnsTrue() {
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
mTileLayout.updateResources(); // setup with 1
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(2);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(2);
assertEquals(true, mTileLayout.updateResources());
}
@Test
public void resourcesSame_updateResources_returnsFalse() {
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
mTileLayout.updateResources(); // setup with 1
assertEquals(false, mTileLayout.updateResources());
@@ -250,7 +255,7 @@ public class TileLayoutTest extends SysuiTestCase {
QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
mTileLayout.addTile(tileRecord);
- FakeTileView tileView = new FakeTileView(mContext);
+ FakeTileView tileView = new FakeTileView(mSpyContext);
QSTile.State state = new QSTile.State();
state.label = "TEST LABEL";
state.secondaryLabel = "TEST SECONDARY LABEL";
@@ -276,9 +281,10 @@ public class TileLayoutTest extends SysuiTestCase {
}
private void changeFontScaling(float scale) {
- Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
+ Configuration configuration =
+ new Configuration(mSpyContext.getResources().getConfiguration());
configuration.fontScale = scale;
// updateConfiguration could help update on both resource configuration and displayMetrics
- mContext.getResources().updateConfiguration(configuration, null, null);
+ mSpyContext.getResources().updateConfiguration(configuration, null, null);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 2cc6709d0f37..d647d6add512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -21,6 +21,7 @@ import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.ContextThemeWrapper
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
@@ -63,6 +64,8 @@ class FooterActionsViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private lateinit var utils: FooterActionsTestUtils
+ private val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+
@Before
fun setUp() {
utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
@@ -84,8 +87,14 @@ class FooterActionsViewModelTest : SysuiTestCase() {
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
)
)
- assertThat(settings.backgroundColor).isEqualTo(R.attr.offStateColor)
- assertThat(settings.iconTint).isNull()
+ assertThat(settings.backgroundColor).isEqualTo(R.attr.shadeInactive)
+ assertThat(settings.iconTint)
+ .isEqualTo(
+ Utils.getColorAttrDefaultColor(
+ themedContext,
+ R.attr.onShadeInactiveVariant,
+ )
+ )
}
@Test
@@ -105,12 +114,12 @@ class FooterActionsViewModelTest : SysuiTestCase() {
ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
)
)
- assertThat(power.backgroundColor).isEqualTo(com.android.internal.R.attr.colorAccent)
+ assertThat(power.backgroundColor).isEqualTo(R.attr.shadeActive)
assertThat(power.iconTint)
.isEqualTo(
Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.textColorOnAccent,
+ themedContext,
+ R.attr.onShadeActive,
),
)
}
@@ -170,7 +179,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
assertThat(userSwitcher).isNotNull()
assertThat(userSwitcher!!.icon)
.isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
- assertThat(userSwitcher.backgroundColor).isEqualTo(R.attr.offStateColor)
+ assertThat(userSwitcher.backgroundColor).isEqualTo(R.attr.shadeInactive)
// Change the current user name.
userSwitcherControllerWrapper.currentUserName = "bar"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 18f3837a7d36..dc0fae53f0c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -75,6 +76,9 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(context.createContextAsUser(any(), anyInt())).thenReturn(context)
+ whenever(context.packageManager).thenReturn(packageManager)
+
// Use the default value set in the ServiceInfo
whenever(packageManager.getComponentEnabledSetting(any()))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
@@ -86,7 +90,6 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
underTest =
InstalledTilesComponentRepositoryImpl(
context,
- packageManager,
testDispatcher,
)
}
@@ -224,6 +227,52 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
assertThat(componentNames).isEmpty()
}
+ @Test
+ fun packageOnlyInSecondaryUser_noException() =
+ testScope.runTest {
+ val userId = 10
+ val secondaryUserContext: Context = mock()
+ whenever(context.userId).thenReturn(0) // System context
+ whenever(context.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
+ .thenReturn(secondaryUserContext)
+
+ val secondaryUserPackageManager: PackageManager = mock()
+ whenever(secondaryUserContext.packageManager).thenReturn(secondaryUserPackageManager)
+
+ // Use the default value set in the ServiceInfo
+ whenever(secondaryUserPackageManager.getComponentEnabledSetting(any()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+ // System User package manager throws exception if the component doesn't exist for that
+ // user
+ whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT))
+ .thenThrow(IllegalArgumentException()) // The package is not in the system user
+
+ val resolveInfo =
+ ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true)
+ // Both package manager should return the same (because the query is for the secondary
+ // user)
+ whenever(
+ secondaryUserPackageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+ whenever(
+ packageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+
+ val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+
+ assertThat(componentNames).containsExactly(TEST_COMPONENT)
+ }
+
private fun getRegisteredReceiver(): BroadcastReceiver {
verify(context)
.registerReceiverAsUser(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 962b53737274..22b1c7b58ab3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -204,6 +204,19 @@ public class QSTileImplTest extends SysuiTestCase {
}
@Test
+ public void testLongClick_falsing() {
+ mFalsingManager.setFalseLongTap(true);
+ mTile.longClick(null /* view */);
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mLongClicked).isFalse();
+
+ mFalsingManager.setFalseLongTap(false);
+ mTile.longClick(null /* view */);
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mLongClicked).isTrue();
+ }
+
+ @Test
public void testSecondaryClick_Metrics() {
mTile.secondaryClick(null /* view */);
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
@@ -518,6 +531,7 @@ public class QSTileImplTest extends SysuiTestCase {
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
boolean mClicked;
+ boolean mLongClicked;
int mRefreshes = 0;
protected TileImpl(
@@ -551,6 +565,11 @@ public class QSTileImplTest extends SysuiTestCase {
}
@Override
+ protected void handleLongClick(@Nullable View view) {
+ mLongClicked = true;
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
mRefreshes++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 28aeba461c50..3c667725c78a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -22,6 +22,7 @@ import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.text.TextUtils
+import android.view.ContextThemeWrapper
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
@@ -386,7 +387,11 @@ class QSTileViewImplTest : SysuiTestCase() {
context: Context,
icon: QSIconView,
collapsed: Boolean
- ) : QSTileViewImpl(context, icon, collapsed) {
+ ) : QSTileViewImpl(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
+ icon,
+ collapsed
+ ) {
fun changeState(state: QSTile.State) {
handleStateChanged(state)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index f0e4e3adda7c..77a443666442 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -25,6 +25,7 @@ import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.ContextThemeWrapper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
@@ -110,7 +111,9 @@ class DndTileTest : SysuiTestCase() {
whenever(qsHost.userId).thenReturn(DEFAULT_USER)
- val wrappedContext = object : ContextWrapper(context) {
+ val wrappedContext = object : ContextWrapper(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ ) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
return sharedPreferences
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index ef7c7bc0844d..9188293dc751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -80,6 +80,7 @@ import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.ui.view.LongPressHandlingView;
@@ -91,7 +92,6 @@ import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -629,7 +629,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mHeadsUpManager);
mNotificationPanelViewController.setTrackingStartedListener(() -> {});
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {}
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 5802eb3d9618..eb4ae1a743ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -514,6 +514,17 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
}
@Test
+ public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
+
+ verify(mKeyguardMediaController).setDozeWakeUpAnimationWaiting(true);
+ }
+
+ @Test
public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2a9b403cb2e6..5fb3a7955b5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -28,6 +28,11 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
+import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dock.DockManager
@@ -35,14 +40,9 @@ import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
-import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -80,9 +80,9 @@ import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -198,10 +198,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 252a03bb07d2..544137e95779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -40,8 +40,8 @@ import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -211,10 +211,10 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index c72f4e77d4eb..a2c291281fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -169,6 +169,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mShadeInteractor =
new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
mDisableFlagsRepository,
mKeyguardRepository,
new FakeUserSetupRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 00a056708f07..729c4a9145c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -56,7 +56,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var assistManager: AssistManager
@Mock private lateinit var gutsManager: NotificationGutsManager
- @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@@ -82,7 +82,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
Lazy { gutsManager },
)
shadeController.setNotificationShadeWindowViewController(nswvc)
- shadeController.setNotificationPanelViewController(notificationPanelViewController)
+ shadeController.setShadeViewController(shadeViewController)
}
@Test
@@ -91,9 +91,9 @@ class ShadeControllerImplTest : SysuiTestCase() {
// Trying to open it does nothing.
shadeController.animateExpandShade()
- verify(notificationPanelViewController, never()).expandToNotifications()
+ verify(shadeViewController, never()).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController, never()).expand(ArgumentMatchers.anyBoolean())
+ verify(shadeViewController, never()).expand(ArgumentMatchers.anyBoolean())
}
@Test
@@ -102,15 +102,15 @@ class ShadeControllerImplTest : SysuiTestCase() {
// Can now be opened.
shadeController.animateExpandShade()
- verify(notificationPanelViewController).expandToNotifications()
+ verify(shadeViewController).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController).expandToQs()
+ verify(shadeViewController).expandToQs()
}
@Test
fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(true)
+ whenever(shadeViewController.isTracking).thenReturn(true)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
@@ -122,7 +122,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Test
fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(false)
+ whenever(shadeViewController.isTracking).thenReturn(false)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 2da2e9238d0a..f542ab099517 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -237,11 +237,22 @@ class ShadeHeaderControllerTest : SysuiTestCase() {
whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(false)
makeShadeVisible()
+ shadeHeaderController.qsExpandedFraction = 1.0f
verify(statusIcons).addIgnoredSlots(carrierIconSlots)
}
@Test
+ fun dualCarrier_enablesCarrierIconsInStatusIcons_qsExpanded() {
+ whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+ shadeHeaderController.qsExpandedFraction = 0.0f
+
+ verify(statusIcons, times(2)).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
fun disableQS_notDisabled_visible() {
makeShadeVisible()
shadeHeaderController.disable(0, 0, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
index 44613103a5b2..dae9c975b997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
@@ -20,8 +20,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
@@ -48,7 +50,9 @@ public class ShadeCarrierTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mTestableLooper = TestableLooper.get(this);
- LayoutInflater inflater = LayoutInflater.from(mContext);
+ Context themedContext =
+ new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_QuickSettings);
+ LayoutInflater inflater = LayoutInflater.from(themedContext);
mContext.ensureTestableResources();
mTestableLooper.runWithLooper(() ->
mShadeCarrier = (ShadeCarrier) inflater.inflate(R.layout.shade_carrier, null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 7392a94a04c2..3ea8f5412b40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -84,6 +84,7 @@ class ShadeInteractorTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
+ featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val refreshUsersScheduler =
RefreshUsersScheduler(
@@ -117,6 +118,7 @@ class ShadeInteractorTest : SysuiTestCase() {
)
underTest =
ShadeInteractor(
+ testScope.backgroundScope,
disableFlagsRepository,
keyguardRepository,
userSetupRepository,
@@ -126,6 +128,20 @@ class ShadeInteractorTest : SysuiTestCase() {
}
@Test
+ fun isShadeEnabled_matchesDisableFlagsRepo() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isShadeEnabled)
+
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(disable2 = DISABLE2_NOTIFICATION_SHADE)
+ assertThat(actual).isFalse()
+
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
fun isExpandToQsEnabled_deviceNotProvisioned_false() =
testScope.runTest {
whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 385d556092a6..1643e174ee13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -16,6 +16,7 @@ package com.android.systemui.statusbar;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
@@ -200,7 +201,7 @@ public class CommandQueueTest extends SysuiTestCase {
mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(0),
+ verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
eq(BACK_DISPOSITION_DEFAULT), eq(false));
verify(mCallbacks).setImeWindowStatus(
eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 21e0f68cff2d..ff2f1065049b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
@@ -11,6 +12,7 @@ import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -19,6 +21,9 @@ import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -28,8 +33,13 @@ import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -60,8 +70,11 @@ private fun <T> anyObject(): T {
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
+ private val testScope = TestScope(StandardTestDispatcher())
+
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
@@ -87,6 +100,15 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
+ private val disableFlagsRepository = FakeDisableFlagsRepository()
+ private val shadeInteractor = ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository = FakeKeyguardRepository(),
+ userSetupRepository = FakeUserSetupRepository(),
+ deviceProvisionedController = mock(),
+ userInteractor = mock(),
+ )
private val powerInteractor = PowerInteractor(
FakePowerRepository(),
FalsingCollectorFake(),
@@ -99,6 +121,10 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Before
fun setup() {
+ // By default, have the shade enabled
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel()
+ testScope.runCurrent()
+
val helper = NotificationTestHelper(
mContext,
mDependency,
@@ -139,6 +165,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
qsTransitionControllerFactory = { qsTransitionController },
activityStarter = activityStarter,
shadeRepository = FakeShadeRepository(),
+ shadeInteractor = shadeInteractor,
powerInteractor = powerInteractor,
)
transitionController.addCallback(transitionControllerCallback)
@@ -214,7 +241,10 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Test
fun testDontGoWhenShadeDisabled() {
- whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
+ testScope.runCurrent()
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
new file mode 100644
index 000000000000..cfbe8e36537d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class CommandParserTest : SysuiTestCase() {
+ private val parser = CommandParser()
+
+ @Test
+ fun registerToken_cannotReuseNames() {
+ parser.flag("-f")
+ assertThrows(IllegalArgumentException::class.java) { parser.flag("-f") }
+ }
+
+ @Test
+ fun unknownToken_throws() {
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("unknown-token")) }
+ }
+
+ @Test
+ fun parseSingleFlag_present() {
+ val flag by parser.flag("-f")
+ parser.parse(listOf("-f"))
+ assertTrue(flag)
+ }
+
+ @Test
+ fun parseSingleFlag_notPresent() {
+ val flag by parser.flag("-f")
+ parser.parse(listOf())
+ assertFalse(flag)
+ }
+
+ @Test
+ fun parseSingleOptionalParam_present() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parse(listOf("-p", "123"))
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseSingleOptionalParam_notPresent() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parse(listOf())
+ assertThat(param).isNull()
+ }
+
+ @Test
+ fun parseSingleOptionalParam_missingArg_throws() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("-p")) }
+ }
+
+ @Test
+ fun parseSingleRequiredParam_present() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ parser.parse(listOf("-p", "123"))
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseSingleRequiredParam_notPresent_failsValidation() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ assertFalse(parser.parse(listOf()))
+ }
+
+ @Test
+ fun parseSingleRequiredParam_missingArg_throws() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("-p")) }
+ }
+
+ @Test
+ fun parseAsSubCommand_singleFlag_present() {
+ val flag by parser.flag("-f")
+ val args = listOf("-f").listIterator()
+ parser.parseAsSubCommand(args)
+
+ assertTrue(flag)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleFlag_notPresent() {
+ val flag by parser.flag("-f")
+ val args = listOf("--other-flag").listIterator()
+ parser.parseAsSubCommand(args)
+
+ assertFalse(flag)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleOptionalParam_present() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parseAsSubCommand(listOf("-p", "123", "--other-arg", "321").listIterator())
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleOptionalParam_notPresent() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parseAsSubCommand(listOf("--other-arg", "321").listIterator())
+ assertThat(param).isNull()
+ }
+
+ @Test
+ fun parseAsSubCommand_singleRequiredParam_present() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ parser.parseAsSubCommand(listOf("-p", "123", "--other-arg", "321").listIterator())
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleRequiredParam_notPresent() {
+ parser.require(parser.param("-p", valueParser = Type.Int))
+ assertFalse(parser.parseAsSubCommand(listOf("--other-arg", "321").listIterator()))
+ }
+
+ @Test
+ fun parseCommandWithSubCommand_required_provided() {
+ val topLevelFlag by parser.flag("flag", shortName = "-f")
+
+ val cmd =
+ object : ParseableCommand("test") {
+ val flag by flag("flag1")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ parser.require(parser.subCommand(cmd))
+ parser.parse(listOf("-f", "test", "--flag1"))
+
+ assertTrue(topLevelFlag)
+ assertThat(cmd).isNotNull()
+ assertTrue(cmd.flag)
+ }
+
+ @Test
+ fun parseCommandWithSubCommand_required_notProvided() {
+ val topLevelFlag by parser.flag("-f")
+
+ val cmd =
+ object : ParseableCommand("test") {
+ val flag by parser.flag("flag1")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ parser.require(parser.subCommand(cmd))
+
+ assertFalse(parser.parse(listOf("-f")))
+ }
+
+ @Test
+ fun flag_requiredParam_optionalParam_allProvided_failsValidation() {
+ val flag by parser.flag("-f")
+ val optionalParam by parser.param("-p", valueParser = Type.Int)
+ val requiredParam by parser.require(parser.param("-p2", valueParser = Type.Boolean))
+
+ parser.parse(
+ listOf(
+ "-f",
+ "-p",
+ "123",
+ "-p2",
+ "false",
+ )
+ )
+
+ assertTrue(flag)
+ assertThat(optionalParam).isEqualTo(123)
+ assertFalse(requiredParam)
+ }
+
+ @Test
+ fun flag_requiredParam_optionalParam_optionalExcluded() {
+ val flag by parser.flag("-f")
+ val optionalParam by parser.param("-p", valueParser = Type.Int)
+ val requiredParam by parser.require(parser.param("-p2", valueParser = Type.Boolean))
+
+ parser.parse(
+ listOf(
+ "-p2",
+ "true",
+ )
+ )
+
+ assertFalse(flag)
+ assertThat(optionalParam).isNull()
+ assertTrue(requiredParam)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
new file mode 100644
index 000000000000..e391d6b11cd6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class ParametersTest : SysuiTestCase() {
+ @Test
+ fun singleArgOptional_returnsNullBeforeParse() {
+ val optional by SingleArgParamOptional(longName = "longName", valueParser = Type.Int)
+ assertThat(optional).isNull()
+ }
+
+ @Test
+ fun singleArgOptional_returnsParsedValue() {
+ val param = SingleArgParamOptional(longName = "longName", valueParser = Type.Int)
+ param.parseArgsFromIter(listOf("3").listIterator())
+ val optional by param
+ assertThat(optional).isEqualTo(3)
+ }
+
+ @Test
+ fun singleArgRequired_throwsBeforeParse() {
+ val req by SingleArgParam(longName = "param", valueParser = Type.Boolean)
+ assertThrows(IllegalStateException::class.java) { req }
+ }
+
+ @Test
+ fun singleArgRequired_returnsParsedValue() {
+ val param = SingleArgParam(longName = "param", valueParser = Type.Boolean)
+ param.parseArgsFromIter(listOf("true").listIterator())
+ val req by param
+ assertTrue(req)
+ }
+
+ @Test
+ fun param_handledAfterParse() {
+ val optParam = SingleArgParamOptional(longName = "string1", valueParser = Type.String)
+ val reqParam = SingleArgParam(longName = "string2", valueParser = Type.Float)
+
+ assertFalse(optParam.handled)
+ assertFalse(reqParam.handled)
+
+ optParam.parseArgsFromIter(listOf("test").listIterator())
+ reqParam.parseArgsFromIter(listOf("1.23").listIterator())
+
+ assertTrue(optParam.handled)
+ assertTrue(reqParam.handled)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
new file mode 100644
index 000000000000..86548d079003
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class ParseableCommandTest : SysuiTestCase() {
+ @Mock private lateinit var pw: PrintWriter
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ /**
+ * A little change-detector-y, but this is just a general assertion that building up a command
+ * parser via its wrapper works as expected.
+ */
+ @Test
+ fun testFactoryMethods() {
+ val mySubCommand =
+ object : ParseableCommand("subCommand") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val mySubCommand2 =
+ object : ParseableCommand("subCommand2") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ // Verify that the underlying parser contains the correct types
+ val myCommand =
+ object : ParseableCommand("testName") {
+ val flag by flag("flag", shortName = "f")
+ val requiredParam by
+ param(longName = "required-param", shortName = "r", valueParser = Type.String)
+ .required()
+ val optionalParam by
+ param(longName = "optional-param", shortName = "o", valueParser = Type.Boolean)
+ val optionalSubCommand by subCommand(mySubCommand)
+ val requiredSubCommand by subCommand(mySubCommand2).required()
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val flags = myCommand.parser.flags
+ val params = myCommand.parser.params
+ val subCommands = myCommand.parser.subCommands
+
+ assertThat(flags).hasSize(2)
+ assertThat(flags[0]).isInstanceOf(Flag::class.java)
+ assertThat(flags[1]).isInstanceOf(Flag::class.java)
+
+ assertThat(params).hasSize(2)
+ val req = params.filter { it is SingleArgParam<*> }
+ val opt = params.filter { it is SingleArgParamOptional<*> }
+ assertThat(req).hasSize(1)
+ assertThat(opt).hasSize(1)
+
+ val reqSub = subCommands.filter { it is RequiredSubCommand<*> }
+ val optSub = subCommands.filter { it is OptionalSubCommand<*> }
+ assertThat(reqSub).hasSize(1)
+ assertThat(optSub).hasSize(1)
+ }
+
+ @Test
+ fun factoryMethods_enforceShortNameRules() {
+ // Short names MUST be one character long
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val flag by flag("longName", "invalidShortName")
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val param by param("longName", "invalidShortName", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+ }
+
+ @Test
+ fun factoryMethods_enforceLongNames_notPrefixed() {
+ // Long names must not start with "-", since they will be added
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val flag by flag("--invalid")
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val param by param("-invalid", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+ }
+
+ @Test
+ fun executeDoesNotPropagateExceptions() {
+ val cmd =
+ object : ParseableCommand("test-command") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val throwingCommand = listOf("unknown-token")
+
+ // Given a command that would cause an ArgParseError
+ assertThrows(ArgParseError::class.java) { cmd.parser.parse(throwingCommand) }
+
+ // The parser consumes that error
+ cmd.execute(pw, throwingCommand)
+ }
+
+ @Test
+ fun executeFailingCommand_callsOnParseFailed() {
+ val cmd =
+ object : ParseableCommand("test-command") {
+ val flag by flag("flag")
+
+ var onParseFailedCalled = false
+
+ override fun execute(pw: PrintWriter) {}
+ override fun onParseFailed(error: ArgParseError) {
+ onParseFailedCalled = true
+ }
+ }
+
+ val throwingCommand = listOf("unknown-token")
+ cmd.execute(pw, throwingCommand)
+
+ assertTrue(cmd.onParseFailedCalled)
+ }
+
+ @Test
+ fun baseCommand() {
+ val myCommand = MyCommand()
+ myCommand.execute(pw, baseCommand)
+
+ assertThat(myCommand.flag1).isFalse()
+ assertThat(myCommand.singleParam).isNull()
+ }
+
+ @Test
+ fun commandWithFlags() {
+ val command = MyCommand()
+ command.execute(pw, cmdWithFlags)
+
+ assertThat(command.flag1).isTrue()
+ assertThat(command.flag2).isTrue()
+ }
+
+ @Test
+ fun commandWithArgs() {
+ val cmd = MyCommand()
+ cmd.execute(pw, cmdWithSingleArgParam)
+
+ assertThat(cmd.singleParam).isEqualTo("single_param")
+ }
+
+ @Test
+ fun commandWithRequiredParam_provided() {
+ val cmd =
+ object : ParseableCommand(name) {
+ val singleRequiredParam: String by
+ param(
+ longName = "param1",
+ shortName = "p",
+ valueParser = Type.String,
+ )
+ .required()
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val cli = listOf("-p", "value")
+ cmd.execute(pw, cli)
+
+ assertThat(cmd.singleRequiredParam).isEqualTo("value")
+ }
+
+ @Test
+ fun commandWithRequiredParam_not_provided_throws() {
+ val cmd =
+ object : ParseableCommand(name) {
+ val singleRequiredParam by
+ param(shortName = "p", longName = "param1", valueParser = Type.String)
+ .required()
+
+ override fun execute(pw: PrintWriter) {}
+
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ parser.parse(args)
+ execute(pw)
+ }
+ }
+
+ val cli = listOf("")
+ assertThrows(ArgParseError::class.java) { cmd.execute(pw, cli) }
+ }
+
+ @Test
+ fun commandWithSubCommand() {
+ val subName = "sub-command"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ val singleOptionalParam: String? by param("param", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val cmd =
+ object : ParseableCommand(name) {
+ val subCmd by subCommand(subCmd)
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ cmd.execute(pw, listOf("sub-command", "--param", "test"))
+ assertThat(cmd.subCmd?.singleOptionalParam).isEqualTo("test")
+ }
+
+ @Test
+ fun complexCommandWithSubCommands_reusedNames() {
+ val commandLine = "-f --param1 arg1 sub-command1 -f -p arg2 --param2 arg3".split(" ")
+
+ val subName = "sub-command1"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ val flag1 by flag("flag", shortName = "f")
+ val param1: String? by param("param1", shortName = "p", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val myCommand =
+ object : ParseableCommand(name) {
+ val flag1 by flag(longName = "flag", shortName = "f")
+ val param1 by param("param1", shortName = "p", valueParser = Type.String).required()
+ val param2: String? by param(longName = "param2", valueParser = Type.String)
+ val subCommand by subCommand(subCmd)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ myCommand.execute(pw, commandLine)
+
+ assertThat(myCommand.flag1).isTrue()
+ assertThat(myCommand.param1).isEqualTo("arg1")
+ assertThat(myCommand.param2).isEqualTo("arg3")
+ assertThat(myCommand.subCommand).isNotNull()
+ assertThat(myCommand.subCommand?.flag1).isTrue()
+ assertThat(myCommand.subCommand?.param1).isEqualTo("arg2")
+ }
+
+ class MyCommand(
+ private val onExecute: ((MyCommand) -> Unit)? = null,
+ ) : ParseableCommand(name) {
+
+ val flag1 by flag(shortName = "f", longName = "flag1", description = "flag 1 for test")
+ val flag2 by flag(shortName = "g", longName = "flag2", description = "flag 2 for test")
+ val singleParam: String? by
+ param(
+ shortName = "a",
+ longName = "arg1",
+ valueParser = Type.String,
+ )
+
+ override fun execute(pw: PrintWriter) {
+ onExecute?.invoke(this)
+ }
+ }
+
+ companion object {
+ const val name = "my_command"
+ val baseCommand = listOf("")
+ val cmdWithFlags = listOf("-f", "--flag2")
+ val cmdWithSingleArgParam = listOf("--arg1", "single_param")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
new file mode 100644
index 000000000000..759f0bcd6ea8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
@@ -0,0 +1,61 @@
+package com.android.systemui.statusbar.commandline
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class ValueParserTest : SysuiTestCase() {
+ @Test
+ fun parseString() {
+ assertThat(Type.String.parseValue("test")).isEqualTo(Result.success("test"))
+ }
+
+ @Test
+ fun parseInt() {
+ assertThat(Type.Int.parseValue("123")).isEqualTo(Result.success(123))
+
+ assertTrue(Type.Int.parseValue("not an Int").isFailure)
+ }
+
+ @Test
+ fun parseFloat() {
+ assertThat(Type.Float.parseValue("1.23")).isEqualTo(Result.success(1.23f))
+
+ assertTrue(Type.Int.parseValue("not a Float").isFailure)
+ }
+
+ @Test
+ fun parseBoolean() {
+ assertThat(Type.Boolean.parseValue("true")).isEqualTo(Result.success(true))
+ assertThat(Type.Boolean.parseValue("false")).isEqualTo(Result.success(false))
+
+ assertTrue(Type.Boolean.parseValue("not a Boolean").isFailure)
+ }
+
+ @Test
+ fun mapToComplexType() {
+ val parseSquare = Type.Int.map { Rect(it, it, it, it) }
+
+ assertThat(parseSquare.parseValue("10")).isEqualTo(Result.success(Rect(10, 10, 10, 10)))
+ }
+
+ @Test
+ fun mapToFallibleComplexType() {
+ val fallibleParseSquare =
+ Type.Int.map {
+ if (it > 0) {
+ Rect(it, it, it, it)
+ } else {
+ null
+ }
+ }
+
+ assertThat(fallibleParseSquare.parseValue("10"))
+ .isEqualTo(Result.success(Rect(10, 10, 10, 10)))
+ assertTrue(fallibleParseSquare.parseValue("-10").isFailure)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 914301f2e830..2af0cebf3519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -69,6 +69,8 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
@Mock private lateinit var listener: SystemStatusAnimationCallback
+ @Mock private lateinit var logger: SystemStatusAnimationSchedulerLogger
+
private lateinit var systemClock: FakeSystemClock
private lateinit var chipAnimationController: SystemEventChipAnimationController
private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler
@@ -538,7 +540,8 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
statusBarWindowController,
dumpManager,
systemClock,
- CoroutineScope(StandardTestDispatcher(testScope.testScheduler))
+ CoroutineScope(StandardTestDispatcher(testScope.testScheduler)),
+ logger
)
// add a mock listener
systemStatusAnimationScheduler.addCallback(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
index a37c38669bfd..902dd51d3a87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
@@ -20,8 +20,8 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.StatusBarState
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
index 7707a7e8bf8c..47c5e5b021ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
@@ -20,8 +20,8 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.stack.StackStateLogger
import com.google.common.truth.Truth
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6ae7dca296c8..ee8325ec02b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -87,6 +87,7 @@ import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -415,6 +416,36 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
+ public void callSwipeCallbacksDuringClearAll() {
+ initController(/* viewIsAttached= */ true);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ NotificationCallback notificationCallback = mController.mNotificationCallback;
+
+ when(mNotificationStackScrollLayout.getClearAllInProgress()).thenReturn(true);
+
+ notificationCallback.onBeginDrag(row);
+ verify(mNotificationStackScrollLayout).onSwipeBegin(row);
+
+ notificationCallback.handleChildViewDismissed(row);
+ verify(mNotificationStackScrollLayout).onSwipeEnd();
+ }
+
+ @Test
+ public void callSwipeCallbacksDuringClearNotification() {
+ initController(/* viewIsAttached= */ true);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ NotificationCallback notificationCallback = mController.mNotificationCallback;
+
+ when(mNotificationStackScrollLayout.getClearAllInProgress()).thenReturn(false);
+
+ notificationCallback.onBeginDrag(row);
+ verify(mNotificationStackScrollLayout).onSwipeBegin(row);
+
+ notificationCallback.handleChildViewDismissed(row);
+ verify(mNotificationStackScrollLayout).onSwipeEnd();
+ }
+
+ @Test
public void testOnMenuClickedLogging() {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 036b8becfbbc..8545b894ad41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -144,23 +145,32 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Test
public void testDisableNotificationShade() {
- when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mCentralSurfaces.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+ // Start with nothing disabled
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NONE, false);
+
when(mCommandQueue.panelsEnabled()).thenReturn(false);
+ // WHEN the new disable flags have the shade disabled
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+ // THEN the shade is collapsed
verify(mShadeController).animateCollapseShade();
}
@Test
public void testEnableNotificationShade() {
- when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mCentralSurfaces.getDisabled2())
- .thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+ // Start with the shade disabled
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+ reset(mShadeController);
+
when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ // WHEN the new disable flags have the shade enabled
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
+
+ // THEN the shade is not collapsed
verify(mShadeController, never()).animateCollapseShade();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 1ffffe4dca75..88d8dfc50b47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -450,7 +450,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
() -> mAssistManager,
() -> mNotificationGutsManager
));
- mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+ mShadeController.setShadeViewController(mNotificationPanelViewController);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
mShadeController.setNotificationPresenter(mNotificationPresenter);
@@ -490,9 +490,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mMetricsLogger,
mShadeLogger,
mUiBgExecutor,
+ mNotificationPanelViewController,
mNotificationMediaManager,
mLockscreenUserManager,
mRemoteInputManager,
+ mQuickSettingsController,
mUserSwitcherController,
mBatteryController,
mColorExtractor,
@@ -587,8 +589,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// TODO: we should be able to call mCentralSurfaces.start() and have all the below values
// initialized automatically and make NPVC private.
mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
- mCentralSurfaces.mShadeSurface = mNotificationPanelViewController;
- mCentralSurfaces.mQsController = mQuickSettingsController;
mCentralSurfaces.mDozeScrimController = mDozeScrimController;
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 3e90ed9811d0..4aac8419af3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -42,6 +42,8 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -135,6 +137,19 @@ class KeyguardBypassControllerTest : SysuiTestCase() {
}
@Test
+ fun onFaceAuthEnabledChanged_notifiesBypassEnabledListeners() {
+ initKeyguardBypassController()
+ val bypassListener = mock(KeyguardBypassController.OnBypassStateChangedListener::class.java)
+ val callback = ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+
+ keyguardBypassController.registerOnBypassStateChangedListener(bypassListener)
+ verify(keyguardStateController).addCallback(callback.capture())
+
+ callback.value.onFaceAuthEnabledChanged()
+ verify(bypassListener).onBypassStateChanged(anyBoolean())
+ }
+
+ @Test
fun configDevicePostureClosed_matchState_isPostureAllowedForFaceAuth_returnTrue() {
defaultConfigPostureClosed()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
new file mode 100644
index 000000000000..47671fbadd0a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.WallpaperManager
+import android.content.pm.UserInfo
+import android.os.Looper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class LockscreenWallpaperTest : SysuiTestCase() {
+
+ private lateinit var underTest: LockscreenWallpaper
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val userRepository = FakeUserRepository()
+
+ private val wallpaperManager: WallpaperManager = mock()
+
+ @Before
+ fun setUp() {
+ whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
+ underTest =
+ LockscreenWallpaper(
+ /* wallpaperManager= */ wallpaperManager,
+ /* iWallpaperManager= */ mock(),
+ /* keyguardUpdateMonitor= */ mock(),
+ /* dumpManager= */ mock(),
+ /* mediaManager= */ mock(),
+ /* mainHandler= */ FakeHandler(Looper.getMainLooper()),
+ /* javaAdapter= */ JavaAdapter(testScope.backgroundScope),
+ /* userRepository= */ userRepository,
+ /* userTracker= */ mock(),
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun getBitmap_matchesUserIdFromUserRepo() =
+ testScope.runTest {
+ val info = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info))
+ userRepository.setSelectedUserInfo(info)
+
+ underTest.bitmap
+
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+
+ @Test
+ fun getBitmap_usesOldUserIfNewUserInProgress() =
+ testScope.runTest {
+ val info5 = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ val info6 = UserInfo(/* id= */ 6, /* name= */ "id6", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info5, info6))
+ userRepository.setSelectedUserInfo(info5)
+
+ // WHEN the selection of user 6 is only in progress
+ userRepository.setSelectedUserInfo(
+ info6,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+
+ underTest.bitmap
+
+ // THEN we still use user 5 for wallpaper selection
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 9b1d93b691c3..5dcb90144b70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -18,10 +18,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.RUNNING_CHIP_ANIM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -49,7 +45,7 @@ import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
-import androidx.core.animation.Animator;
+import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -85,6 +81,7 @@ import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -139,6 +136,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @ClassRule
+ public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>();
@@ -172,7 +171,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
- when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -192,24 +190,26 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+ // WHEN the system event animation starts
+ fragment.onSystemEventAnimationBegin().start();
+
+ // THEN the view remains invisible during the animation
+ assertEquals(0f, getEndSideContentView().getAlpha(), 0.01);
+ mAnimatorTestRule.advanceTimeBy(500);
+ assertEquals(0f, getEndSideContentView().getAlpha(), 0.01);
+
// WHEN the disable flags are cleared during a system event animation
- when(mAnimationScheduler.getAnimationState()).thenReturn(RUNNING_CHIP_ANIM);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- // THEN the view is made visible again, but still low alpha
- assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ // THEN the view remains invisible
assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
// WHEN the system event animation finishes
- when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
- Animator anim = fragment.onSystemEventAnimationFinish(false);
- anim.start();
- processAllMessages();
- anim.end();
+ fragment.onSystemEventAnimationFinish(false).start();
+ mAnimatorTestRule.advanceTimeBy(500);
// THEN the system info is full alpha
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -219,20 +219,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the system event animation finishes
- when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
- Animator anim = fragment.onSystemEventAnimationFinish(false);
- anim.start();
- processAllMessages();
- anim.end();
+ fragment.onSystemEventAnimationFinish(false).start();
+ mAnimatorTestRule.advanceTimeBy(500);
- // THEN the system info is at full alpha, but still INVISIBLE (since the disable flag is
- // still set)
- assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ // THEN the system info remains invisible (since the disable flag is still set)
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -241,15 +236,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+
// WHEN the system event animation begins
- Animator anim = fragment.onSystemEventAnimationBegin();
- anim.start();
- processAllMessages();
- anim.end();
+ fragment.onSystemEventAnimationBegin().start();
+ mAnimatorTestRule.advanceTimeBy(500);
- // THEN the system info is visible but alpha 0
- assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ // THEN the system info is invisible
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
}
@@ -257,25 +251,21 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+
// WHEN the system event animation begins
- Animator anim = fragment.onSystemEventAnimationBegin();
- anim.start();
- processAllMessages();
- anim.end();
+ fragment.onSystemEventAnimationBegin().start();
+ mAnimatorTestRule.advanceTimeBy(500);
- // THEN the system info is visible but alpha 0
- assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ // THEN the system info is invisible
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
// WHEN the system event animation finishes
- when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
- anim = fragment.onSystemEventAnimationFinish(false);
- anim.start();
- processAllMessages();
- anim.end();
+ fragment.onSystemEventAnimationFinish(false).start();
+ mAnimatorTestRule.advanceTimeBy(500);
- // THEN the syste info is full alpha and VISIBLE
+ // THEN the system info is full alpha and VISIBLE
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
new file mode 100644
index 000000000000..2617613d1fc5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.fragment
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.core.animation.AnimatorTestRule
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_SOURCE_1 = 1
+private const val TEST_SOURCE_2 = 2
+private const val TEST_ANIMATION_DURATION = 100L
+private const val INITIAL_ALPHA = 1f
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class MultiSourceMinAlphaControllerTest : SysuiTestCase() {
+
+ private val view = View(context)
+ private val multiSourceMinAlphaController =
+ MultiSourceMinAlphaController(view, initialAlpha = INITIAL_ALPHA)
+
+ @get:Rule val animatorTestRule = AnimatorTestRule()
+
+ @Before
+ fun setup() {
+ multiSourceMinAlphaController.reset()
+ }
+
+ @Test
+ fun testSetAlpha() {
+ multiSourceMinAlphaController.setAlpha(alpha = 0.5f, sourceId = TEST_SOURCE_1)
+ assertEquals(0.5f, view.alpha)
+ }
+
+ @Test
+ fun testAnimateToAlpha() {
+ multiSourceMinAlphaController.animateToAlpha(
+ alpha = 0.5f,
+ sourceId = TEST_SOURCE_1,
+ duration = TEST_ANIMATION_DURATION
+ )
+ animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION)
+ assertEquals(0.5f, view.alpha)
+ }
+
+ @Test
+ fun testReset() {
+ multiSourceMinAlphaController.animateToAlpha(
+ alpha = 0.5f,
+ sourceId = TEST_SOURCE_1,
+ duration = TEST_ANIMATION_DURATION
+ )
+ multiSourceMinAlphaController.setAlpha(alpha = 0.7f, sourceId = TEST_SOURCE_2)
+ multiSourceMinAlphaController.reset()
+ // advance time to ensure that animators are cancelled when the controller is reset
+ animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION)
+ assertEquals(INITIAL_ALPHA, view.alpha)
+ }
+
+ @Test
+ fun testMinOfTwoSourcesIsApplied() {
+ multiSourceMinAlphaController.setAlpha(alpha = 0f, sourceId = TEST_SOURCE_1)
+ multiSourceMinAlphaController.setAlpha(alpha = 0.5f, sourceId = TEST_SOURCE_2)
+ assertEquals(0f, view.alpha)
+ multiSourceMinAlphaController.setAlpha(alpha = 1f, sourceId = TEST_SOURCE_1)
+ assertEquals(0.5f, view.alpha)
+ }
+
+ @Test
+ fun testSetAlphaForSameSourceCancelsAnimator() {
+ multiSourceMinAlphaController.animateToAlpha(
+ alpha = 0f,
+ sourceId = TEST_SOURCE_1,
+ duration = TEST_ANIMATION_DURATION
+ )
+ animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION / 2)
+ multiSourceMinAlphaController.setAlpha(alpha = 1f, sourceId = TEST_SOURCE_1)
+ animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION / 2)
+ // verify that animation was cancelled and the setAlpha call overrides the alpha value of
+ // the animation
+ assertEquals(1f, view.alpha)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 74bcdeec25cd..862eb001becc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -73,7 +73,6 @@ import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -588,11 +587,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
- fun testConnectionRepository_invalidSubId_throws() =
+ fun testConnectionRepository_invalidSubId_doesNotThrow() =
testScope.runTest {
- assertThrows(IllegalArgumentException::class.java) {
- underTest.getRepoForSubId(SUB_1_ID)
- }
+ underTest.getRepoForSubId(SUB_1_ID)
+ // No exception
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 6301fa0be463..842d548c8358 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -48,7 +48,11 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val interactor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index d787ada90a73..5cabcd4163b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -43,6 +44,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -66,6 +68,9 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
@Mock
private KeyguardUpdateMonitorLogger mLogger;
+ @Captor
+ private ArgumentCaptor<KeyguardUpdateMonitorCallback> mUpdateCallbackCaptor;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -84,6 +89,23 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
}
@Test
+ public void testFaceAuthEnabledChanged_calledWhenFaceEnrollmentStateChanges() {
+ KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
+
+ when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(false);
+ verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture());
+ mKeyguardStateController.addCallback(callback);
+ assertThat(mKeyguardStateController.isFaceAuthEnabled()).isFalse();
+
+ when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true);
+ mUpdateCallbackCaptor.getValue().onBiometricEnrollmentStateChanged(
+ BiometricSourceType.FACE);
+
+ assertThat(mKeyguardStateController.isFaceAuthEnabled()).isTrue();
+ verify(callback).onFaceAuthEnabledChanged();
+ }
+
+ @Test
public void testIsShowing() {
assertThat(mKeyguardStateController.isShowing()).isFalse();
mKeyguardStateController.notifyKeyguardState(true /* showing */, false /* occluded */);
@@ -177,16 +199,14 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
@Test
public void testOnEnabledTrustAgentsChangedCallback() {
final Random random = new Random();
- final ArgumentCaptor<KeyguardUpdateMonitorCallback> updateCallbackCaptor =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
- verify(mKeyguardUpdateMonitor).registerCallback(updateCallbackCaptor.capture());
+ verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture());
final KeyguardStateController.Callback stateCallback =
mock(KeyguardStateController.Callback.class);
mKeyguardStateController.addCallback(stateCallback);
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
- updateCallbackCaptor.getValue().onEnabledTrustAgentsChanged(random.nextInt());
+ mUpdateCallbackCaptor.getValue().onEnabledTrustAgentsChanged(random.nextInt());
verify(stateCallback).onUnlockedChanged();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 079fbcd0304c..0c28cbb52831 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -26,6 +26,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -224,6 +226,40 @@ class UserRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
+ underTest = create(this)
+ var selectedUser: SelectedUserModel? = null
+ underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
+ setUpUsers(count = 2, selectedIndex = 1)
+
+ // WHEN the user is changing
+ tracker.onUserChanging(userId = 1)
+
+ // THEN the selection status is IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN the user has finished changing
+ tracker.onUserChanged(userId = 1)
+
+ // THEN the selection status is COMPLETE
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ tracker.onProfileChanged()
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ setUpUsers(count = 2, selectedIndex = 0)
+
+ tracker.onUserChanging(userId = 0)
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN a profile change occurs while a user is changing
+ tracker.onProfileChanged()
+
+ // THEN the selection status remains as IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ @Test
fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest {
underTest = create(this)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 738f09ddce3d..2715aaa82253 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -41,7 +41,9 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
fun setDetectionStatus(status: DetectionStatus) {
_detectionStatus.value = status
}
- override val isLockedOut = MutableStateFlow(false)
+
+ private val _isLockedOut = MutableStateFlow(false)
+ override val isLockedOut = _isLockedOut
private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null)
val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
_runningAuthRequest.asStateFlow()
@@ -56,6 +58,10 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
_isAuthRunning.value = true
}
+ fun setLockedOut(value: Boolean) {
+ _isLockedOut.value = value
+ }
+
override fun cancel() {
_isAuthRunning.value = false
_runningAuthRequest.value = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
new file mode 100644
index 000000000000..312ade510784
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Helper to create a new KeyguardTransitionInteractor in a way that doesn't require modifying 20+
+ * tests whenever we add a constructor param.
+ */
+object KeyguardTransitionInteractorFactory {
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ scope: CoroutineScope,
+ repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
+ ): WithDependencies {
+ return WithDependencies(
+ repository = repository,
+ KeyguardTransitionInteractor(
+ scope = scope,
+ repository = repository,
+ )
+ )
+ }
+
+ data class WithDependencies(
+ val repository: KeyguardTransitionRepository,
+ val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 61e5b5fc27ea..51ee0c00cb0d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -19,18 +19,28 @@ package com.android.systemui.user.data.repository
import android.content.pm.UserInfo
import android.os.UserHandle
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.yield
class FakeUserRepository : UserRepository {
companion object {
// User id to represent a non system (human) user id. We presume this is the main user.
private const val MAIN_USER_ID = 10
+
+ private val DEFAULT_SELECTED_USER = 0
+ private val DEFAULT_SELECTED_USER_INFO =
+ UserInfo(
+ /* id= */ DEFAULT_SELECTED_USER,
+ /* name= */ "default selected user",
+ /* flags= */ 0,
+ )
}
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -40,8 +50,11 @@ class FakeUserRepository : UserRepository {
private val _userInfos = MutableStateFlow<List<UserInfo>>(emptyList())
override val userInfos: Flow<List<UserInfo>> = _userInfos.asStateFlow()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
+ override val selectedUser =
+ MutableStateFlow(
+ SelectedUserModel(DEFAULT_SELECTED_USER_INFO, SelectionStatus.SELECTION_COMPLETE)
+ )
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
private val _userSwitchingInProgress = MutableStateFlow(false)
override val userSwitchingInProgress: Flow<Boolean>
@@ -72,7 +85,7 @@ class FakeUserRepository : UserRepository {
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -87,12 +100,15 @@ class FakeUserRepository : UserRepository {
_userInfos.value = infos
}
- suspend fun setSelectedUserInfo(userInfo: UserInfo) {
+ suspend fun setSelectedUserInfo(
+ userInfo: UserInfo,
+ selectionStatus: SelectionStatus = SelectionStatus.SELECTION_COMPLETE,
+ ) {
check(_userInfos.value.contains(userInfo)) {
"Cannot select the following user, it is not in the list of user infos: $userInfo!"
}
- _selectedUserInfo.value = userInfo
+ selectedUser.value = SelectedUserModel(userInfo, selectionStatus)
yield()
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index c2ebddf00fb4..502ee4db80c8 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -75,6 +75,7 @@ import android.util.Range;
import android.util.Size;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
@@ -204,7 +205,7 @@ public class CameraExtensionsProxyService extends Service {
* A per-process global camera extension manager instance, to track and
* initialize/release extensions depending on client activity.
*/
- private static final class CameraExtensionManagerGlobal {
+ private static final class CameraExtensionManagerGlobal implements IBinder.DeathRecipient {
private static final String TAG = "CameraExtensionManagerGlobal";
private final int EXTENSION_DELAY_MS = 1000;
@@ -212,8 +213,9 @@ public class CameraExtensionsProxyService extends Service {
private final HandlerThread mHandlerThread;
private final Object mLock = new Object();
- private long mCurrentClientCount = 0;
- private ArraySet<Long> mActiveClients = new ArraySet<>();
+ private ArraySet<IBinder> mActiveClients = new ArraySet<>();
+ private HashMap<IBinder, ArraySet<IBinder.DeathRecipient>> mClientDeathRecipient =
+ new HashMap<>();
private IInitializeSessionCallback mInitializeCb = null;
// Singleton instance
@@ -314,8 +316,20 @@ public class CameraExtensionsProxyService extends Service {
return GLOBAL_CAMERA_MANAGER;
}
- public long registerClient(Context ctx) {
+ public boolean registerClient(Context ctx, IBinder token) {
synchronized (mLock) {
+ if (mActiveClients.contains(token)) {
+ Log.e(TAG, "Failed to register existing client!");
+ return false;
+ }
+
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to link to binder token!");
+ return false;
+ }
+
if (INIT_API_SUPPORTED) {
if (mActiveClients.isEmpty()) {
InitializerFuture status = new InitializerFuture();
@@ -327,47 +341,80 @@ public class CameraExtensionsProxyService extends Service {
TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timed out while initializing camera extensions!");
- return -1;
+ return false;
}
if (!initSuccess) {
Log.e(TAG, "Failed while initializing camera extensions!");
- return -1;
+ return false;
}
}
}
- long ret = mCurrentClientCount;
- mCurrentClientCount++;
- if (mCurrentClientCount < 0) {
- mCurrentClientCount = 0;
- }
- mActiveClients.add(ret);
+ mActiveClients.add(token);
+ mClientDeathRecipient.put(token, new ArraySet<>());
- return ret;
+ return true;
}
}
- public void unregisterClient(long clientId) {
+ public void unregisterClient(IBinder token) {
synchronized (mLock) {
- if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
- INIT_API_SUPPORTED) {
- InitializerFuture status = new InitializerFuture();
- InitializerImpl.deinit(new ReleaseHandler(status),
- new HandlerExecutor(mHandler));
- boolean releaseSuccess;
- try {
- releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- Log.e(TAG, "Timed out while releasing camera extensions!");
- return;
- }
- if (!releaseSuccess) {
- Log.e(TAG, "Failed while releasing camera extensions!");
+ if (mActiveClients.remove(token)) {
+ token.unlinkToDeath(this, 0);
+ mClientDeathRecipient.remove(token);
+ if (mActiveClients.isEmpty() && INIT_API_SUPPORTED) {
+ InitializerFuture status = new InitializerFuture();
+ InitializerImpl.deinit(new ReleaseHandler(status),
+ new HandlerExecutor(mHandler));
+ boolean releaseSuccess;
+ try {
+ releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Timed out while releasing camera extensions!");
+ return;
+ }
+ if (!releaseSuccess) {
+ Log.e(TAG, "Failed while releasing camera extensions!");
+ }
}
}
}
}
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder who) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(who)) {
+ mClientDeathRecipient.get(who).stream().forEach(
+ recipient -> recipient.binderDied(who));
+ }
+ unregisterClient(who);
+ }
+ }
+
+ public void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(token)) {
+ ArraySet<IBinder.DeathRecipient> recipients = mClientDeathRecipient.get(token);
+ recipients.add(recipient);
+ }
+ }
+ }
+
+ public void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(token)) {
+ ArraySet<IBinder.DeathRecipient> recipients = mClientDeathRecipient.get(token);
+ recipients.remove(recipient);
+ }
+ }
+ }
+
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -406,21 +453,35 @@ public class CameraExtensionsProxyService extends Service {
/**
* @hide
*/
- private static long registerClient(Context ctx) {
+ private static boolean registerClient(Context ctx, IBinder token) {
if (!EXTENSIONS_PRESENT) {
- return -1;
+ return false;
}
- return CameraExtensionManagerGlobal.get().registerClient(ctx);
+ return CameraExtensionManagerGlobal.get().registerClient(ctx, token);
}
/**
* @hide
*/
- public static void unregisterClient(long clientId) {
+ public static void unregisterClient(IBinder token) {
if (!EXTENSIONS_PRESENT) {
return;
}
- CameraExtensionManagerGlobal.get().unregisterClient(clientId);
+ CameraExtensionManagerGlobal.get().unregisterClient(token);
+ }
+
+ /**
+ * @hide
+ */
+ private static void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ CameraExtensionManagerGlobal.get().registerDeathRecipient(token, recipient);
+ }
+
+ /**
+ * @hide
+ */
+ private static void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ CameraExtensionManagerGlobal.get().unregisterDeathRecipient(token, recipient);
}
/**
@@ -649,13 +710,14 @@ public class CameraExtensionsProxyService extends Service {
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
@Override
- public long registerClient() {
- return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this);
+ public boolean registerClient(IBinder token) {
+ return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this,
+ token);
}
@Override
- public void unregisterClient(long clientId) {
- CameraExtensionsProxyService.unregisterClient(clientId);
+ public void unregisterClient(IBinder token) {
+ CameraExtensionsProxyService.unregisterClient(token);
}
private boolean checkCameraPermission() {
@@ -1192,16 +1254,18 @@ public class CameraExtensionsProxyService extends Service {
}
}
- private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
+ private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub implements
+ IBinder.DeathRecipient {
private final SessionProcessorImpl mSessionProcessor;
private String mCameraId = null;
+ private IBinder mToken;
public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
mSessionProcessor = sessionProcessor;
}
@Override
- public CameraSessionConfig initSession(String cameraId,
+ public CameraSessionConfig initSession(IBinder token, String cameraId,
Map<String, CameraMetadataNative> charsMapNative, OutputSurface previewSurface,
OutputSurface imageCaptureSurface, OutputSurface postviewSurface) {
OutputSurfaceImplStub outputPreviewSurfaceImpl =
@@ -1253,12 +1317,14 @@ public class CameraExtensionsProxyService extends Service {
ret.sessionParameter = initializeParcelableMetadata(
sessionConfig.getSessionParameters(), cameraId);
mCameraId = cameraId;
-
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
return ret;
}
@Override
- public void deInitSession() {
+ public void deInitSession(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mSessionProcessor.deInitSession();
}
@@ -1330,6 +1396,11 @@ public class CameraExtensionsProxyService extends Service {
return null;
}
+
+ @Override
+ public void binderDied() {
+ mSessionProcessor.deInitSession();
+ }
}
private class OutputSurfaceConfigurationImplStub implements OutputSurfaceConfigurationImpl {
@@ -1395,24 +1466,31 @@ public class CameraExtensionsProxyService extends Service {
}
}
- private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
+ private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements
+ IBinder.DeathRecipient {
private final PreviewExtenderImpl mPreviewExtender;
private String mCameraId = null;
+ private boolean mSessionEnabled;
+ private IBinder mToken;
public PreviewExtenderImplStub(PreviewExtenderImpl previewExtender) {
mPreviewExtender = previewExtender;
}
@Override
- public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
+ public void onInit(IBinder token, String cameraId,
+ CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
mCameraManager.registerDeviceStateListener(chars);
mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
}
@Override
- public void onDeInit() {
+ public void onDeInit(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mPreviewExtender.onDeInit();
}
@@ -1423,11 +1501,13 @@ public class CameraExtensionsProxyService extends Service {
@Override
public CaptureStageImpl onEnableSession() {
+ mSessionEnabled = true;
return initializeParcelable(mPreviewExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
+ mSessionEnabled = false;
return initializeParcelable(mPreviewExtender.onDisableSession(), mCameraId);
}
@@ -1516,26 +1596,41 @@ public class CameraExtensionsProxyService extends Service {
}
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mSessionEnabled) {
+ mPreviewExtender.onDisableSession();
+ }
+ mPreviewExtender.onDeInit();
+ }
}
- private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub {
+ private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub implements
+ IBinder.DeathRecipient {
private final ImageCaptureExtenderImpl mImageExtender;
private String mCameraId = null;
+ private boolean mSessionEnabled;
+ private IBinder mToken;
public ImageCaptureExtenderImplStub(ImageCaptureExtenderImpl imageExtender) {
mImageExtender = imageExtender;
}
@Override
- public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
+ public void onInit(IBinder token, String cameraId,
+ CameraMetadataNative cameraCharacteristics) {
CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
mCameraManager.registerDeviceStateListener(chars);
mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
mCameraId = cameraId;
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
}
@Override
- public void onDeInit() {
+ public void onDeInit(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mImageExtender.onDeInit();
}
@@ -1564,11 +1659,13 @@ public class CameraExtensionsProxyService extends Service {
@Override
public CaptureStageImpl onEnableSession() {
+ mSessionEnabled = true;
return initializeParcelable(mImageExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
+ mSessionEnabled = false;
return initializeParcelable(mImageExtender.onDisableSession(), mCameraId);
}
@@ -1737,6 +1834,14 @@ public class CameraExtensionsProxyService extends Service {
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mSessionEnabled) {
+ mImageExtender.onDisableSession();
+ }
+ mImageExtender.onDeInit();
+ }
}
private class ProcessResultCallback implements ProcessResultImpl {
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index b5e8214dd2c0..299d33fbbe6d 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -39,8 +39,12 @@
"name": "FrameworksCoreTests",
"options": [
{
- "include-filter": "android.accessibilityservice",
- "include-filter": "android.view.accessibility",
+ "include-filter": "android.accessibilityservice"
+ },
+ {
+ "include-filter": "android.view.accessibility"
+ },
+ {
"include-filter": "com.android.internal.accessibility"
},
{
@@ -74,8 +78,12 @@
"name": "FrameworksCoreTests",
"options": [
{
- "include-filter": "android.accessibilityservice",
- "include-filter": "android.view.accessibility",
+ "include-filter": "android.accessibilityservice"
+ },
+ {
+ "include-filter": "android.view.accessibility"
+ },
+ {
"include-filter": "com.android.internal.accessibility"
}
]
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index c937447a67e4..05b6eb4a64c1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -319,7 +319,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
void unbindImeLocked(AbstractAccessibilityServiceConnection connection);
- void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
+ void attachAccessibilityOverlayToDisplay(
+ int interactionId,
+ int displayId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback);
}
@@ -2654,17 +2658,26 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
@Override
- public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) {
+ public void attachAccessibilityOverlayToDisplay(
+ int interactionId,
+ int displayId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback) {
final long identity = Binder.clearCallingIdentity();
try {
- mSystemSupport.attachAccessibilityOverlayToDisplay(displayId, sc);
+ mSystemSupport.attachAccessibilityOverlayToDisplay(
+ interactionId, displayId, sc, callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public void attachAccessibilityOverlayToWindow(int accessibilityWindowId, SurfaceControl sc)
+ public void attachAccessibilityOverlayToWindow(
+ int interactionId,
+ int accessibilityWindowId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback)
throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
@@ -2677,14 +2690,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mSystemSupport.getCurrentUserIdLocked(),
resolveAccessibilityWindowIdLocked(accessibilityWindowId));
if (connection == null) {
- Slog.e(LOG_TAG, "unable to get remote accessibility connection.");
+ callback.sendAttachOverlayResult(
+ AccessibilityService.OVERLAY_RESULT_INVALID, interactionId);
return;
}
- connection.getRemote().attachAccessibilityOverlayToWindow(sc);
+ connection
+ .getRemote()
+ .attachAccessibilityOverlayToWindow(sc, interactionId, callback);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
-} \ No newline at end of file
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 466cda3f2131..cbf4cced0e9b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -131,6 +131,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowAttributes;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IWindowMagnificationConnection;
@@ -5343,16 +5344,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
- public void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc) {
+ public void attachAccessibilityOverlayToDisplay(
+ int interactionId,
+ int displayId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback) {
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::attachAccessibilityOverlayToDisplayInternal,
this,
+ interactionId,
displayId,
- sc));
+ sc,
+ callback));
}
- void attachAccessibilityOverlayToDisplayInternal(int displayId, SurfaceControl sc) {
+ void attachAccessibilityOverlayToDisplayInternal(
+ int interactionId,
+ int displayId,
+ SurfaceControl sc,
+ IAccessibilityInteractionConnectionCallback callback) {
+ int result;
if (!mA11yOverlayLayers.contains(displayId)) {
mA11yOverlayLayers.put(displayId, mWindowManagerService.getA11yOverlayLayer(displayId));
}
@@ -5360,10 +5372,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (parent == null) {
Slog.e(LOG_TAG, "Unable to get accessibility overlay SurfaceControl.");
mA11yOverlayLayers.remove(displayId);
- return;
+ result = AccessibilityService.OVERLAY_RESULT_INVALID;
+ } else {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.reparent(sc, parent).setTrustedOverlay(sc, true).apply();
+ t.close();
+ result = AccessibilityService.OVERLAY_RESULT_SUCCESS;
+ }
+ // Send the result back to the service.
+ try {
+ callback.sendAttachOverlayResult(result, interactionId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Exception while attaching overlay.", re);
+ // the other side will time out
}
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.reparent(sc, parent).setTrustedOverlay(sc, true).apply();
- t.close();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index c08b6ab56f55..a525e7c64bc3 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityService;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Slog;
@@ -281,4 +282,11 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
info.setDismissable(mNodeWithReplacementActions.isDismissable());
}
}
+
+ @Override
+ public void sendAttachOverlayResult(
+ @AccessibilityService.AttachOverlayResult int result, int interactionId)
+ throws RemoteException {
+ mServiceCallback.sendAttachOverlayResult(result, interactionId);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index b1cdc5053c27..ba3d4340a157 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -319,6 +319,10 @@ public class FullScreenMagnificationController implements
FullScreenMagnificationController::onUserContextChanged,
FullScreenMagnificationController.this, mDisplayId);
mControllerCtx.getHandler().sendMessage(m);
+
+ synchronized (mLock) {
+ refreshThumbnail();
+ }
}
@Override
@@ -344,7 +348,7 @@ public class FullScreenMagnificationController implements
mMagnificationRegion.set(magnified);
mMagnificationRegion.getBounds(mMagnificationBounds);
- refreshThumbnail(getScale(), getCenterX(), getCenterY());
+ refreshThumbnail();
// It's possible that our magnification spec is invalid with the new bounds.
// Adjust the current spec's offsets if necessary.
@@ -602,13 +606,13 @@ public class FullScreenMagnificationController implements
}
@GuardedBy("mLock")
- void refreshThumbnail(float scale, float centerX, float centerY) {
+ void refreshThumbnail() {
if (mMagnificationThumbnail != null) {
mMagnificationThumbnail.setThumbnailBounds(
mMagnificationBounds,
- scale,
- centerX,
- centerY
+ getScale(),
+ getCenterX(),
+ getCenterY()
);
}
}
@@ -627,7 +631,7 @@ public class FullScreenMagnificationController implements
// We call refreshThumbnail when the thumbnail is just created to set current
// magnification bounds to thumbnail. It to prevent the thumbnail size has not yet
// updated properly and thus shows with huge size. (b/276314641)
- refreshThumbnail(getScale(), getCenterX(), getCenterY());
+ refreshThumbnail();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
index 03fa93d8a3bc..a7bdd5a09ac2 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
@@ -99,15 +99,17 @@ public class MagnificationThumbnail {
Log.d(LOG_TAG, "setThumbnailBounds " + currentBounds);
}
mHandler.post(() -> {
- mWindowBounds = currentBounds;
- setBackgroundBounds();
+ refreshBackgroundBounds(currentBounds);
if (mVisible) {
updateThumbnailMainThread(scale, centerX, centerY);
}
});
}
- private void setBackgroundBounds() {
+ @MainThread
+ private void refreshBackgroundBounds(Rect currentBounds) {
+ mWindowBounds = currentBounds;
+
Point magnificationBoundary = getMagnificationThumbnailPadding(mContext);
mThumbnailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
mThumbnailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
@@ -117,6 +119,10 @@ public class MagnificationThumbnail {
mBackgroundParams.height = mThumbnailHeight;
mBackgroundParams.x = initX;
mBackgroundParams.y = initY;
+
+ if (mVisible) {
+ mWindowManager.updateViewLayout(mThumbnailLayout, mBackgroundParams);
+ }
}
@MainThread
@@ -264,21 +270,16 @@ public class MagnificationThumbnail {
mThumbnailView.setScaleX(scaleDown);
mThumbnailView.setScaleY(scaleDown);
}
- float thumbnailWidth;
- float thumbnailHeight;
- if (mThumbnailView.getWidth() == 0 || mThumbnailView.getHeight() == 0) {
- // if the thumbnail view size is not updated correctly, we just use the cached values.
- thumbnailWidth = mThumbnailWidth;
- thumbnailHeight = mThumbnailHeight;
- } else {
- thumbnailWidth = mThumbnailView.getWidth();
- thumbnailHeight = mThumbnailView.getHeight();
- }
- if (!Float.isNaN(centerX)) {
+
+ if (!Float.isNaN(centerX)
+ && !Float.isNaN(centerY)
+ && mThumbnailWidth > 0
+ && mThumbnailHeight > 0
+ ) {
var padding = mThumbnailView.getPaddingTop();
var ratio = 1f / BG_ASPECT_RATIO;
- var centerXScaled = centerX * ratio - (thumbnailWidth / 2f + padding);
- var centerYScaled = centerY * ratio - (thumbnailHeight / 2f + padding);
+ var centerXScaled = centerX * ratio - (mThumbnailWidth / 2f + padding);
+ var centerYScaled = centerY * ratio - (mThumbnailHeight / 2f + padding);
if (DEBUG) {
Log.d(
diff --git a/services/autofill/OWNERS b/services/autofill/OWNERS
index edfb2112198a..4f170ca65337 100644
--- a/services/autofill/OWNERS
+++ b/services/autofill/OWNERS
@@ -1 +1,20 @@
+# Bug component: 351486
+
include /core/java/android/view/autofill/OWNERS
+
+# co-own by both teams
+per-file Session.java = file:/core/java/android/view/autofill/OWNERS
+per-file Session.java = wangqi@google.com
+per-file AutofillManagerService*. = file:/core/java/android/view/autofill/OWNERS
+per-file Session.java = wangqi@google.com
+per-file AutofillManagerService* = file:/core/java/android/view/autofill/OWNERS
+per-file AutofillManagerService* = wangqi@google.com
+per-file *FillUI* = file:/core/java/android/view/autofill/OWNERS
+per-file *FillUI* = wangqi@google.com
+
+# Bug component: 543785 = per-file *Augmented*
+per-file *Augmented* = file:/core/java/android/service/autofill/augmented/OWNERS
+
+# Bug component: 543785 = per-file *Inline*
+per-file *Inline* = file:/core/java/android/widget/inline/OWNERS
+
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dc6f8584006e..9026c20fe529 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.Manifest.permission.BATTERY_STATS;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.POWER_SAVER;
@@ -96,6 +95,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.CpuScalingPolicyReader;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
@@ -152,6 +153,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private static IBatteryStats sService;
private final PowerProfile mPowerProfile;
+ private final CpuScalingPolicies mCpuScalingPolicies;
final BatteryStatsImpl mStats;
final CpuWakeupStats mCpuWakeupStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
@@ -368,14 +370,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler = new Handler(mHandlerThread.getLooper());
mPowerProfile = new PowerProfile(context);
+ mCpuScalingPolicies = new CpuScalingPolicyReader().read();
mStats = new BatteryStatsImpl(systemDir, handler, this,
- this, mUserManagerUserInfoProvider);
+ this, mUserManagerUserInfoProvider, mPowerProfile, mCpuScalingPolicies);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
- mStats.setPowerProfileLocked(mPowerProfile);
final boolean resetOnUnplugHighBatteryLevel = context.getResources().getBoolean(
com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
@@ -2916,8 +2918,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
- mUserManagerUserInfoProvider);
- checkinStats.setPowerProfileLocked(mPowerProfile);
+ mUserManagerUserInfoProvider, mPowerProfile,
+ mCpuScalingPolicies);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpProtoLocked(
@@ -2957,8 +2959,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
- mUserManagerUserInfoProvider);
- checkinStats.setPowerProfileLocked(mPowerProfile);
+ mUserManagerUserInfoProvider, mPowerProfile,
+ mCpuScalingPolicies);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckin(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 7ee96aaca21d..a20623cd1ee9 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -26,6 +26,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.PendingIntent;
import android.app.PendingIntentStats;
@@ -126,6 +127,18 @@ public class PendingIntentController {
}
}
Bundle.setDefusable(bOptions, true);
+ ActivityOptions opts = ActivityOptions.fromBundle(bOptions);
+ if (opts != null && opts.getPendingIntentBackgroundActivityStartMode()
+ != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ Slog.wtf(TAG, "Resetting option setPendingIntentBackgroundActivityStartMode("
+ + opts.getPendingIntentBackgroundActivityStartMode()
+ + ") to SYSTEM_DEFINED from the options provided by the pending "
+ + "intent creator ("
+ + packageName
+ + ") because this option is meant for the pending intent sender");
+ opts.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+ }
final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
@@ -135,7 +148,7 @@ public class PendingIntentController {
PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
token, resultWho, requestCode, intents, resolvedTypes, flags,
- SafeActivityOptions.fromBundle(bOptions), userId);
+ new SafeActivityOptions(opts), userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 4eaee4aa7b79..79292fec1ec2 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -456,6 +456,20 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
// can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
+ if (opts.getPendingIntentCreatorBackgroundActivityStartMode()
+ != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ Slog.wtf(TAG,
+ "Resetting option "
+ + "setPendingIntentCreatorBackgroundActivityStartMode("
+ + opts.getPendingIntentCreatorBackgroundActivityStartMode()
+ + ") to SYSTEM_DEFINED from the options provided by the "
+ + "pending intent sender ("
+ + key.packageName
+ + ") because this option is meant for the pending intent "
+ + "creator");
+ opts.setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+ }
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 5f8e211c42c3..09df2770776d 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -51,11 +51,11 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Counter;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.anr.AnrLatencyTracker;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Counter;
import com.android.server.ResourcePressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
@@ -458,6 +458,11 @@ class ProcessErrorStateRecord {
String currentPsiState = ResourcePressureUtil.currentPsiState();
latencyTracker.currentPsiStateReturned();
report.append(currentPsiState);
+ // The 'processCpuTracker' variable is a shared resource that might be initialized and
+ // updated in a different thread. In order to prevent thread visibility issues, which
+ // can occur when one thread does not immediately see the changes made to
+ // 'processCpuTracker' by another thread, it is necessary to use synchronization whenever
+ // 'processCpuTracker' is accessed or modified.
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// We push the native pids collection task to the helper thread through
@@ -519,12 +524,16 @@ class ProcessErrorStateRecord {
}
mService.updateCpuStatsNow();
mService.mAppProfiler.printCurrentCpuState(report, anrTime);
- info.append(processCpuTracker.printCurrentLoad());
+ synchronized (processCpuTracker) {
+ info.append(processCpuTracker.printCurrentLoad());
+ }
info.append(report);
}
report.append(tracesFileException.getBuffer());
- info.append(processCpuTracker.printCurrentState(anrTime));
+ synchronized (processCpuTracker) {
+ info.append(processCpuTracker.printCurrentState(anrTime));
+ }
Slog.e(TAG, info.toString());
if (tracesFile == null) {
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index fc3d04f5adeb..c1374e1b5a05 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -464,28 +464,31 @@ public class StackTracesDumpHelper {
latencyTracker.processCpuTrackerMethodsCalled();
}
ArrayList<Integer> extraPids = new ArrayList<>();
- processCpuTracker.init();
+ synchronized (processCpuTracker) {
+ processCpuTracker.init();
+ }
try {
Thread.sleep(200);
} catch (InterruptedException ignored) {
}
- processCpuTracker.update();
+ synchronized (processCpuTracker) {
+ processCpuTracker.update();
+ // We'll take the stack crawls of just the top apps using CPU.
+ final int workingStatsNumber = processCpuTracker.countWorkingStats();
+ for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
+ ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
+ if (lastPids.indexOfKey(stats.pid) >= 0) {
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ }
- // We'll take the stack crawls of just the top apps using CPU.
- final int workingStatsNumber = processCpuTracker.countWorkingStats();
- for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
- ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
- if (lastPids.indexOfKey(stats.pid) >= 0) {
- if (DEBUG_ANR) {
- Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ extraPids.add(stats.pid);
+ } else {
+ Slog.i(TAG,
+ "Skipping next CPU consuming process, not a java proc: "
+ + stats.pid);
}
-
- extraPids.add(stats.pid);
- } else {
- Slog.i(TAG,
- "Skipping next CPU consuming process, not a java proc: "
- + stats.pid);
}
}
if (latencyTracker != null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index dae9c5361099..d426c797b981 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1994,7 +1994,7 @@ class UserController implements Handler.Callback {
}
private void dismissUserSwitchDialog(Runnable onDismissed) {
- mInjector.dismissUserSwitchingDialog(onDismissed);
+ mUiHandler.post(() -> mInjector.dismissUserSwitchingDialog(onDismissed));
}
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4a5a3e0ed00..ca15dd79adbc 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -2166,7 +2166,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
synchronized (mUidObserverLock) {
- if (ActivityManager.isProcStateBackground(procState)) {
+ if (procState != ActivityManager.PROCESS_STATE_TOP) {
disableGameMode(uid);
return;
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 50ffbcb9f4c4..3f4f981dc0b8 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -957,6 +957,7 @@ public final class PlaybackActivityMonitor
players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
}
mFadingManager.unfadeOutUid(uid, players);
+ mDuckingManager.unduckUid(uid, players);
}
//=================================================================
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6b69e1caa985..79c50804ac5f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -280,15 +279,21 @@ public class Vpn {
private static final int VPN_DEFAULT_SCORE = 101;
/**
- * The reset session timer for data stall. If a session has not successfully revalidated after
- * the delay, the session will be torn down and restarted in an attempt to recover. Delay
+ * The recovery timer for data stall. If a session has not successfully revalidated after
+ * the delay, the session will perform MOBIKE or be restarted in an attempt to recover. Delay
* counter is reset on successful validation only.
*
+ * <p>The first {@code MOBIKE_RECOVERY_ATTEMPT} timers are used for performing MOBIKE.
+ * System will perform session reset for the remaining timers.
* <p>If retries have exceeded the length of this array, the last entry in the array will be
* used as a repeating interval.
*/
- private static final long[] DATA_STALL_RESET_DELAYS_SEC = {30L, 60L, 120L, 240L, 480L, 960L};
-
+ private static final long[] DATA_STALL_RECOVERY_DELAYS_MS =
+ {1000L, 5000L, 30000L, 60000L, 120000L, 240000L, 480000L, 960000L};
+ /**
+ * Maximum attempts to perform MOBIKE when the network is bad.
+ */
+ private static final int MAX_MOBIKE_RECOVERY_ATTEMPT = 2;
/**
* The initial token value of IKE session.
*/
@@ -392,7 +397,6 @@ public class Vpn {
private final UserManager mUserManager;
private final VpnProfileStore mVpnProfileStore;
- protected boolean mDataStallSuspected = false;
@VisibleForTesting
VpnProfileStore getVpnProfileStore() {
@@ -685,14 +689,14 @@ public class Vpn {
}
/**
- * Get the length of time to wait before resetting the ike session when a data stall is
- * suspected.
+ * Get the length of time to wait before perform data stall recovery when the validation
+ * result is bad.
*/
- public long getDataStallResetSessionSeconds(int count) {
- if (count >= DATA_STALL_RESET_DELAYS_SEC.length) {
- return DATA_STALL_RESET_DELAYS_SEC[DATA_STALL_RESET_DELAYS_SEC.length - 1];
+ public long getValidationFailRecoveryMs(int count) {
+ if (count >= DATA_STALL_RECOVERY_DELAYS_MS.length) {
+ return DATA_STALL_RECOVERY_DELAYS_MS[DATA_STALL_RECOVERY_DELAYS_MS.length - 1];
} else {
- return DATA_STALL_RESET_DELAYS_SEC[count];
+ return DATA_STALL_RECOVERY_DELAYS_MS[count];
}
}
@@ -3044,7 +3048,6 @@ public class Vpn {
@Nullable private IkeSessionWrapper mSession;
@Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
- @Nullable private VpnConnectivityDiagnosticsCallback mDiagnosticsCallback;
// mMobikeEnabled can only be updated after IKE AUTH is finished.
private boolean mMobikeEnabled = false;
@@ -3055,7 +3058,7 @@ public class Vpn {
* <p>This variable controls the retry delay, and is reset when the VPN pass network
* validation.
*/
- private int mDataStallRetryCount = 0;
+ private int mValidationFailRetryCount = 0;
/**
* The number of attempts since the last successful connection.
@@ -3136,15 +3139,6 @@ public class Vpn {
mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
new Handler(mLooper));
}
-
- // DiagnosticsCallback may return more than one alive VPNs, but VPN will filter based on
- // Network object.
- final NetworkRequest diagRequest = new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_VPN)
- .removeCapability(NET_CAPABILITY_NOT_VPN).build();
- mDiagnosticsCallback = new VpnConnectivityDiagnosticsCallback();
- mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
- diagRequest, mExecutor, mDiagnosticsCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -3875,39 +3869,12 @@ public class Vpn {
}
}
- class VpnConnectivityDiagnosticsCallback
- extends ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
- // The callback runs in the executor thread.
- @Override
- public void onDataStallSuspected(
- ConnectivityDiagnosticsManager.DataStallReport report) {
- synchronized (Vpn.this) {
- // Ignore stale runner.
- if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
-
- // Handle the report only for current VPN network. If data stall is already
- // reported, ignoring the other reports. It means that the stall is not
- // recovered by MOBIKE and should be on the way to reset the ike session.
- if (mNetworkAgent != null
- && mNetworkAgent.getNetwork().equals(report.getNetwork())
- && !mDataStallSuspected) {
- Log.d(TAG, "Data stall suspected");
-
- // Trigger MOBIKE.
- maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
- mDataStallSuspected = true;
- }
- }
- }
- }
-
public void onValidationStatus(int status) {
mEventChanges.log("[Validation] validation status " + status);
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
// No data stall now. Reset it.
mExecutor.execute(() -> {
- mDataStallSuspected = false;
- mDataStallRetryCount = 0;
+ mValidationFailRetryCount = 0;
if (mScheduledHandleDataStallFuture != null) {
Log.d(TAG, "Recovered from stall. Cancel pending reset action.");
mScheduledHandleDataStallFuture.cancel(false /* mayInterruptIfRunning */);
@@ -3918,8 +3885,21 @@ public class Vpn {
// Skip other invalid status if the scheduled recovery exists.
if (mScheduledHandleDataStallFuture != null) return;
+ if (mValidationFailRetryCount < MAX_MOBIKE_RECOVERY_ATTEMPT) {
+ Log.d(TAG, "Validation failed");
+
+ // Trigger MOBIKE to recover first.
+ mExecutor.schedule(() -> {
+ maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
+ }, mDeps.getValidationFailRecoveryMs(mValidationFailRetryCount++),
+ TimeUnit.MILLISECONDS);
+ return;
+ }
+
+ // Data stall is not recovered by MOBIKE. Try to reset session to recover it.
mScheduledHandleDataStallFuture = mExecutor.schedule(() -> {
- if (mDataStallSuspected) {
+ // Only perform the recovery when the network is still bad.
+ if (mValidationFailRetryCount > 0) {
Log.d(TAG, "Reset session to recover stalled network");
// This will reset old state if it exists.
startIkeSession(mActiveNetwork);
@@ -3928,7 +3908,9 @@ public class Vpn {
// Reset mScheduledHandleDataStallFuture since it's already run on executor
// thread.
mScheduledHandleDataStallFuture = null;
- }, mDeps.getDataStallResetSessionSeconds(mDataStallRetryCount++), TimeUnit.SECONDS);
+ // TODO: compute the delay based on the last recovery timestamp
+ }, mDeps.getValidationFailRecoveryMs(mValidationFailRetryCount++),
+ TimeUnit.MILLISECONDS);
}
}
@@ -4231,8 +4213,6 @@ public class Vpn {
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
- mDiagnosticsCallback);
clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
@@ -5216,7 +5196,7 @@ public class Vpn {
pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
pw.println("Profile: " + runner.mProfile);
pw.println("Token: " + runner.mCurrentToken);
- if (mDataStallSuspected) pw.println("Data stall suspected");
+ pw.println("Validation failed retry count:" + runner.mValidationFailRetryCount);
if (runner.mScheduledHandleDataStallFuture != null) {
pw.println("Reset session scheduled");
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index f9c53c641c1c..1012bc1828c0 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -172,6 +172,7 @@ public final class DeviceStateManagerService extends SystemService {
private DeviceState mRearDisplayState;
// TODO(259328837) Generalize for all pending feature requests in the future
+ @GuardedBy("mLock")
@Nullable
private OverrideRequest mRearDisplayPendingOverrideRequest;
@@ -779,7 +780,7 @@ public final class DeviceStateManagerService extends SystemService {
* {@link StatusBarManagerInternal} to notify SystemUI to display the educational dialog.
*/
@GuardedBy("mLock")
- private void showRearDisplayEducationalOverlayLocked(OverrideRequest request) {
+ private void showRearDisplayEducationalOverlayLocked(@NonNull OverrideRequest request) {
mRearDisplayPendingOverrideRequest = request;
StatusBarManagerInternal statusBar =
@@ -844,8 +845,8 @@ public final class DeviceStateManagerService extends SystemService {
* request if it was dismissed in a way that should cancel the feature.
*/
private void onStateRequestOverlayDismissedInternal(boolean shouldCancelRequest) {
- if (mRearDisplayPendingOverrideRequest != null) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ if (mRearDisplayPendingOverrideRequest != null) {
if (shouldCancelRequest) {
ProcessRecord processRecord = mProcessRecords.get(
mRearDisplayPendingOverrideRequest.getPid());
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 47cde1517450..5b11cfe7ff06 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -18,29 +18,42 @@ package com.android.server.display;
import android.hardware.display.BrightnessInfo;
import android.os.IBinder;
+import android.provider.DeviceConfigInterface;
+
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.function.BooleanSupplier;
class BrightnessRangeController {
- private static final boolean NBM_FEATURE_FLAG = false;
-
private final HighBrightnessModeController mHbmController;
private final NormalBrightnessModeController mNormalBrightnessModeController =
new NormalBrightnessModeController();
private final Runnable mModeChangeCallback;
+ private final boolean mUseNbmController;
+
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback) {
+ this(hbmController, modeChangeCallback,
+ new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+ }
+
+ BrightnessRangeController(HighBrightnessModeController hbmController,
+ Runnable modeChangeCallback, DeviceConfigParameterProvider configParameterProvider) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
+ mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
}
-
void dump(PrintWriter pw) {
+ pw.println("BrightnessRangeController:");
+ pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
mHbmController.dump(pw);
+ mNormalBrightnessModeController.dump(pw);
+
}
void onAmbientLuxChange(float ambientLux) {
@@ -90,7 +103,7 @@ class BrightnessRangeController {
float getCurrentBrightnessMax() {
- if (NBM_FEATURE_FLAG && mHbmController.getHighBrightnessMode()
+ if (mUseNbmController && mHbmController.getHighBrightnessMode()
== BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF) {
return Math.min(mHbmController.getCurrentBrightnessMax(),
mNormalBrightnessModeController.getCurrentBrightnessMax());
@@ -111,7 +124,7 @@ class BrightnessRangeController {
}
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
- if (NBM_FEATURE_FLAG) {
+ if (mUseNbmController) {
boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
hbmChangesFunc.run();
// if nbm transition changed - trigger callback
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index cfdcd636904b..c421ec04d6f5 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -22,7 +22,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IThermalEventListener;
@@ -38,6 +37,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -63,7 +63,7 @@ class BrightnessThrottler {
private final Runnable mThrottlingChangeCallback;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final DeviceConfigListener mDeviceConfigListener;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private int mThrottlingStatus;
@@ -118,7 +118,7 @@ class BrightnessThrottler {
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mUniqueDisplayId = uniqueDisplayId;
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigListener = new DeviceConfigListener();
mThermalBrightnessThrottlingDataId = throttlingDataId;
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
@@ -145,7 +145,7 @@ class BrightnessThrottler {
void stop() {
mSkinThermalStatusObserver.stopObserving();
- mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
+ mConfigParameterProvider.removeOnPropertiesChangedListener(mDeviceConfigListener);
// We're asked to stop throttling, so reset brightness restrictions.
mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -248,12 +248,6 @@ class BrightnessThrottler {
mSkinThermalStatusObserver.dump(pw);
}
- private String getThermalBrightnessThrottlingDataString() {
- return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA,
- /* defaultValue= */ null);
- }
-
// The brightness throttling data id may or may not be specified in the string that is passed
// in, if there is none specified, we assume it is for the default case. Each string passed in
// here must be for one display and one throttling id.
@@ -318,7 +312,8 @@ class BrightnessThrottler {
private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
new HashMap<>(1);
- mThermalBrightnessThrottlingDataString = getThermalBrightnessThrottlingDataString();
+ mThermalBrightnessThrottlingDataString =
+ mConfigParameterProvider.getBrightnessThrottlingData();
boolean validConfig = true;
mThermalBrightnessThrottlingDataOverride.clear();
if (mThermalBrightnessThrottlingDataString != null) {
@@ -390,8 +385,7 @@ class BrightnessThrottler {
public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- mExecutor, this);
+ mConfigParameterProvider.addOnPropertiesChangedListener(mExecutor, this);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7e8771e6d13d..1a8e2db303b3 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -24,7 +24,6 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
-import static android.hardware.display.DisplayManager.HDR_OUTPUT_CONTROL_FLAG;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -42,7 +41,6 @@ import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
-import static android.provider.DeviceConfig.NAMESPACE_DISPLAY_MANAGER;
import android.Manifest;
import android.annotation.NonNull;
@@ -116,7 +114,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -152,6 +150,7 @@ import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.utils.SensorUtils;
@@ -506,6 +505,8 @@ public final class DisplayManagerService extends SystemService {
private final BrightnessSynchronizer mBrightnessSynchronizer;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
+
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -558,6 +559,7 @@ public final class DisplayManagerService extends SystemService {
mWideColorSpace = colorSpaces[1];
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
+ mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
}
public void setupSchedulerPolicies() {
@@ -694,11 +696,11 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mSystemReady = true;
- mIsHdrOutputControlEnabled = isDeviceConfigHdrOutputControlEnabled();
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_DISPLAY_MANAGER,
- BackgroundThread.getExecutor(),
+ mIsHdrOutputControlEnabled =
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled();
+ mConfigParameterProvider.addOnPropertiesChangedListener(BackgroundThread.getExecutor(),
properties -> mIsHdrOutputControlEnabled =
- isDeviceConfigHdrOutputControlEnabled());
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled());
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
@@ -729,12 +731,6 @@ public final class DisplayManagerService extends SystemService {
mContext.registerReceiver(mIdleModeReceiver, filter);
}
- private boolean isDeviceConfigHdrOutputControlEnabled() {
- return DeviceConfig.getBoolean(NAMESPACE_DISPLAY_MANAGER,
- HDR_OUTPUT_CONTROL_FLAG,
- true);
- }
-
@VisibleForTesting
Handler getDisplayHandler() {
return mHandler;
@@ -3145,8 +3141,7 @@ public final class DisplayManagerService extends SystemService {
+ "display: " + display.getDisplayIdLocked());
return null;
}
- if (DeviceConfig.getBoolean("display_manager",
- "use_newly_structured_display_power_controller", true)) {
+ if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ffecf2b7018d..7fc6cd0bebd6 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2672,10 +2672,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
+ float clampedBrightnessValue = clampScreenBrightness(brightnessValue);
mBrightnessSetting.setUserSerial(userSerial);
- mBrightnessSetting.setBrightness(brightnessValue);
+ mBrightnessSetting.setBrightness(clampedBrightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
- float nits = convertToNits(brightnessValue);
+ float nits = convertToNits(clampedBrightnessValue);
if (nits >= 0) {
mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7043af863301..37b9d5638dbc 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -2185,7 +2185,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
@Override
public void setBrightness(float brightnessValue, int userSerial) {
- mDisplayBrightnessController.setBrightness(brightnessValue, userSerial);
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightnessValue),
+ userSerial);
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index dec9f62c8739..b00b7a1f0e2a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -496,7 +496,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
- mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
+ mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
mIsFirstDisplay);
// Load brightness HWC quirk
@@ -1336,6 +1336,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
public SurfaceControlProxy getSurfaceControlProxy() {
return new SurfaceControlProxy();
}
+
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ }
}
public interface DisplayEventListener {
diff --git a/services/core/java/com/android/server/display/NormalBrightnessModeController.java b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
index dbabc2441224..135ebd8f4fbf 100644
--- a/services/core/java/com/android/server/display/NormalBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
@@ -21,6 +21,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayDeviceConfig.BrightnessLimitMapType;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +61,14 @@ class NormalBrightnessModeController {
return recalculateMaxBrightness();
}
+ void dump(PrintWriter pw) {
+ pw.println("NormalBrightnessModeController:");
+ pw.println(" mAutoBrightnessEnabled=" + mAutoBrightnessEnabled);
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mMaxBrightness=" + mMaxBrightness);
+ pw.println(" mMaxBrightnessLimits=" + mMaxBrightnessLimits);
+ }
+
private boolean recalculateMaxBrightness() {
float foundAmbientBoundary = Float.MAX_VALUE;
float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
new file mode 100644
index 000000000000..feebdf1b9799
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.feature;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
+import android.util.Slog;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class to access all DeviceConfig features for display_manager namespace
+ *
+ **/
+public class DeviceConfigParameterProvider {
+
+ private static final String TAG = "DisplayFeatureProvider";
+
+ private final DeviceConfigInterface mDeviceConfig;
+
+ public DeviceConfigParameterProvider(DeviceConfigInterface deviceConfig) {
+ mDeviceConfig = deviceConfig;
+ }
+
+ // feature: revamping_display_power_controller_feature
+ // parameter: use_newly_structured_display_power_controller
+ public boolean isNewPowerControllerFeatureEnabled() {
+ return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true);
+ }
+
+ // feature: hdr_output_control
+ // parameter: enable_hdr_output_control
+ public boolean isHdrOutputControlFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.HDR_OUTPUT_CONTROL_FLAG, true);
+ }
+
+ // feature: flexible_brightness_range_feature
+ // parameter: normal_brightness_mode_controller_enabled
+ public boolean isNormalBrightnessControllerFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER, false);
+ }
+
+ // feature: smooth_display_feature
+ // parameter: peak_refresh_rate_default
+ public float getPeakRefreshRateDefault() {
+ return mDeviceConfig.getFloat(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
+ }
+
+ // Test parameters
+ // usage e.g.: adb shell device_config put display_manager refresh_rate_in_hbm_sunlight 90
+
+ // allows to customize brightness throttling data
+ public String getBrightnessThrottlingData() {
+ return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, null);
+ }
+
+ public int getRefreshRateInHbmSunlight() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, -1);
+ }
+
+ public int getRefreshRateInHbmHdr() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR, -1);
+ }
+
+
+ public int getRefreshRateInHighZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE, -1);
+ }
+
+ public int getRefreshRateInLowZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** add property change listener to DeviceConfig */
+ public void addOnPropertiesChangedListener(Executor executor,
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ executor, listener);
+ }
+
+ /** remove property change listener from DeviceConfig */
+ public void removeOnPropertiesChangedListener(
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.removeOnPropertiesChangedListener(listener);
+ }
+
+ @Nullable
+ private int[] getIntArrayProperty(String prop) {
+ String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+ null);
+
+ if (strArray != null) {
+ return parseIntArray(strArray);
+ }
+ return null;
+ }
+
+ @Nullable
+ private int[] parseIntArray(@NonNull String strArray) {
+ String[] items = strArray.split(",");
+ int[] array = new int[items.length];
+
+ try {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Integer.parseInt(items[i]);
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
+ array = null;
+ }
+
+ return array;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 18895788e4ec..11e35ce09c28 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -20,6 +20,7 @@ import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLU
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID;
+import android.annotation.IntegerRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -68,6 +69,7 @@ import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.SensorUtils;
@@ -84,6 +86,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
+import java.util.function.IntSupplier;
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
@@ -117,7 +120,7 @@ public class DisplayModeDirector {
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@GuardedBy("mLock")
@@ -157,7 +160,7 @@ public class DisplayModeDirector {
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
mAppRequestObserver = new AppRequestObserver();
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mSettingsObserver = new SettingsObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
@@ -681,9 +684,9 @@ public class DisplayModeDirector {
synchronized (mLock) {
mDefaultDisplayDeviceConfig = displayDeviceConfig;
mSettingsObserver.setRefreshRates(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
}
@@ -1087,7 +1090,7 @@ public class DisplayModeDirector {
// reading from the DeviceConfig is an intensive IO operation and having it in the
// startup phase where we thrive to keep the latency very low has significant impact.
setRefreshRates(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
/**
@@ -1095,8 +1098,8 @@ public class DisplayModeDirector {
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ boolean attemptReadFromFeatureParams) {
+ setDefaultPeakRefreshRate(displayDeviceConfig, attemptReadFromFeatureParams);
mDefaultRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultRefreshRate)
@@ -1113,9 +1116,9 @@ public class DisplayModeDirector {
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
this);
- Float deviceConfigDefaultPeakRefresh =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
- if (deviceConfigDefaultPeakRefresh != null) {
+ float deviceConfigDefaultPeakRefresh =
+ mConfigParameterProvider.getPeakRefreshRateDefault();
+ if (deviceConfigDefaultPeakRefresh != -1) {
mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
}
@@ -1137,7 +1140,7 @@ public class DisplayModeDirector {
synchronized (mLock) {
if (defaultPeakRefreshRate == null) {
setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
updateRefreshRateSettingLocked();
} else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
mDefaultPeakRefreshRate = defaultPeakRefreshRate;
@@ -1171,18 +1174,17 @@ public class DisplayModeDirector {
}
private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- Float defaultPeakRefreshRate = null;
+ boolean attemptReadFromFeatureParams) {
+ float defaultPeakRefreshRate = -1;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
- defaultPeakRefreshRate =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+ defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
} catch (Exception exception) {
// Do nothing
}
}
- if (defaultPeakRefreshRate == null) {
+ if (defaultPeakRefreshRate == -1) {
defaultPeakRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultPeakRefreshRate)
@@ -1528,7 +1530,7 @@ public class DisplayModeDirector {
mHandler = handler;
mInjector = injector;
updateBlockingZoneThresholds(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
@@ -1537,10 +1539,10 @@ public class DisplayModeDirector {
* This is used to update the blocking zone thresholds from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ public void updateBlockingZoneThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadLowBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadHighBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
}
@VisibleForTesting
@@ -1579,20 +1581,20 @@ public class DisplayModeDirector {
return mRefreshRateInLowZone;
}
- private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadRefreshRateInHighZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadRefreshRateInLowZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ private void loadLowBrightnessThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadRefreshRateInHighZone(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadRefreshRateInLowZone(displayDeviceConfig, attemptReadFromFeatureParams);
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1604,55 +1606,55 @@ public class DisplayModeDirector {
}
private void loadRefreshRateInLowZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInLowZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInZone)
- : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInLowZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInLowZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
- refreshRateInLowZone);
+ refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInLowZone == -1) {
+ refreshRateInLowZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInZone)
+ : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
+ }
mRefreshRateInLowZone = refreshRateInLowZone;
}
private void loadRefreshRateInHighZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInHighZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_fixedRefreshRateInHighZone) : displayDeviceConfig
- .getDefaultHighBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInHighZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInHighZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- refreshRateInHighZone);
+ refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInHighZone == -1) {
+ refreshRateInHighZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_fixedRefreshRateInHighZone)
+ : displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
+ }
mRefreshRateInHighZone = refreshRateInHighZone;
}
private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1668,10 +1670,10 @@ public class DisplayModeDirector {
Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
Callable<int[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams) {
int[] brightnessThresholds = null;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
brightnessThresholds =
loadFromDeviceConfigDisplaySettingsCallable.call();
@@ -1715,9 +1717,9 @@ public class DisplayModeDirector {
// DeviceConfig is accessible after system ready.
int[] lowDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
int[] lowAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
&& lowDisplayBrightnessThresholds.length
@@ -1727,9 +1729,9 @@ public class DisplayModeDirector {
}
int[] highDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
int[] highAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
&& highDisplayBrightnessThresholds.length
@@ -1738,14 +1740,12 @@ public class DisplayModeDirector {
mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
}
- final int refreshRateInLowZone = mDeviceConfigDisplaySettings
- .getRefreshRateInLowZone();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
if (refreshRateInLowZone != -1) {
mRefreshRateInLowZone = refreshRateInLowZone;
}
- final int refreshRateInHighZone = mDeviceConfigDisplaySettings
- .getRefreshRateInHighZone();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
if (refreshRateInHighZone != -1) {
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -1799,15 +1799,15 @@ public class DisplayModeDirector {
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1822,7 +1822,7 @@ public class DisplayModeDirector {
// from there.
synchronized (mLock) {
loadRefreshRateInLowZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInLowZone) {
@@ -1843,15 +1843,15 @@ public class DisplayModeDirector {
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1866,7 +1866,7 @@ public class DisplayModeDirector {
// from there.
synchronized (mLock) {
loadRefreshRateInHighZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInHighZone) {
@@ -2675,113 +2675,55 @@ public class DisplayModeDirector {
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ mConfigParameterProvider.addOnPropertiesChangedListener(
BackgroundThread.getExecutor(), this);
}
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
- }
-
- public int getRefreshRateInLowZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
-
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ private int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmHdr(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
+ R.integer.config_defaultRefreshRateInHbmHdr,
+ displayDeviceConfig
+ );
}
- public int getRefreshRateInHighZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- -1);
+ private int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmSunlight(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
+ R.integer.config_defaultRefreshRateInHbmSunlight,
+ displayDeviceConfig
+ );
}
- public int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInHbmHdr)
- : displayDeviceConfig.getDefaultRefreshRateInHbmHdr();
+ private int getRefreshRate(IntSupplier fromConfigPram, IntSupplier fromDisplayDeviceConfig,
+ @IntegerRes int configKey, DisplayDeviceConfig displayDeviceConfig) {
+ int refreshRate = -1;
try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
- refreshRate);
- } catch (NullPointerException e) {
+ refreshRate = fromConfigPram.getAsInt();
+ } catch (NullPointerException npe) {
// Do Nothing
}
- return refreshRate;
- }
-
- public int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources()
- .getInteger(R.integer.config_defaultRefreshRateInHbmSunlight)
- : displayDeviceConfig.getDefaultRefreshRateInHbmSunlight();
- try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
- refreshRate);
- } catch (NullPointerException e) {
- // Do Nothing
+ if (refreshRate == -1) {
+ refreshRate = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(configKey)
+ : fromDisplayDeviceConfig.getAsInt();
}
return refreshRate;
}
- /*
- * Return null if no such property
- */
- public Float getDefaultPeakRefreshRate() {
- float defaultPeakRefreshRate = mDeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
-
- if (defaultPeakRefreshRate == -1) {
- return null;
- }
- return defaultPeakRefreshRate;
- }
-
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+ float defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
- defaultPeakRefreshRate).sendToTarget();
+ defaultPeakRefreshRate == -1 ? null : defaultPeakRefreshRate).sendToTarget();
- int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
- final int refreshRateInLowZone = getRefreshRateInLowZone();
+ int[] lowDisplayBrightnessThresholds =
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
+ int[] lowAmbientBrightnessThresholds =
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
@@ -2790,9 +2732,11 @@ public class DisplayModeDirector {
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
0).sendToTarget();
- int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
- final int refreshRateInHighZone = getRefreshRateInHighZone();
+ int[] highDisplayBrightnessThresholds =
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
+ int[] highAmbientBrightnessThresholds =
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
@@ -2814,33 +2758,6 @@ public class DisplayModeDirector {
.sendToTarget();
}
}
-
- private int[] getIntArrayProperty(String prop) {
- String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
- null);
-
- if (strArray != null) {
- return parseIntArray(strArray);
- }
-
- return null;
- }
-
- private int[] parseIntArray(@NonNull String strArray) {
- String[] items = strArray.split(",");
- int[] array = new int[items.length];
-
- try {
- for (int i = 0; i < array.length; i++) {
- array[i] = Integer.parseInt(items[i]);
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
- array = null;
- }
-
- return array;
- }
}
interface Injector {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 6d70d21e3b84..633bf73120e1 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,11 +16,11 @@
package com.android.server.dreams;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import android.app.ActivityTaskManager;
import android.app.BroadcastOptions;
+import android.app.IAppTask;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -214,6 +214,27 @@ final class DreamController {
}
/**
+ * Provides an appTask for the dream with token {@code dreamToken}, so that the dream controller
+ * can stop the dream task when necessary.
+ */
+ void setDreamAppTask(Binder dreamToken, IAppTask appTask) {
+ if (mCurrentDream == null || mCurrentDream.mToken != dreamToken
+ || mCurrentDream.mAppTask != null) {
+ Slog.e(TAG, "Illegal dream activity start. mCurrentDream.mToken = "
+ + mCurrentDream.mToken + ", illegal dreamToken = " + dreamToken
+ + ". Ending this dream activity.");
+ try {
+ appTask.finishAndRemoveTask();
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Unable to stop illegal dream activity.");
+ }
+ return;
+ }
+
+ mCurrentDream.mAppTask = appTask;
+ }
+
+ /**
* Stops dreaming.
*
* The current dream, if any, and any unstopped previous dreams are stopped. The device stops
@@ -303,8 +324,14 @@ final class DreamController {
mSentStartBroadcast = false;
}
- mActivityTaskManager.removeRootTasksWithActivityTypes(
- new int[] {ACTIVITY_TYPE_DREAM});
+ if (mCurrentDream != null && mCurrentDream.mAppTask != null) {
+ // Finish the dream task in case it hasn't finished by itself already.
+ try {
+ mCurrentDream.mAppTask.finishAndRemoveTask();
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Unable to stop dream activity.");
+ }
+ }
mListener.onDreamStopped(dream.mToken);
}
@@ -364,6 +391,7 @@ final class DreamController {
public final boolean mIsPreviewMode;
public final boolean mCanDoze;
public final int mUserId;
+ public IAppTask mAppTask;
public PowerManager.WakeLock mWakeLock;
public boolean mBound;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 68cf59f0ae1f..d88fe8a6c201 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -27,6 +27,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.IAppTask;
import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,6 +38,7 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -116,6 +118,7 @@ public final class DreamManagerService extends SystemService {
private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final ActivityTaskManagerInternal mAtmInternal;
+ private final PackageManagerInternal mPmInternal;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final DreamUiEventLogger mDreamUiEventLogger;
@@ -216,6 +219,7 @@ public final class DreamManagerService extends SystemService {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
+ mPmInternal = getLocalService(PackageManagerInternal.class);
mUserManager = context.getSystemService(UserManager.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, DOZE_WAKE_LOCK_TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
@@ -1064,6 +1068,64 @@ public final class DreamManagerService extends SystemService {
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override // Binder call
+ public void startDreamActivity(@NonNull Intent intent) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ // We post here, because startDreamActivity and setDreamAppTask have to run
+ // synchronously and DreamController#setDreamAppTask has to run on mHandler.
+ mHandler.post(() -> {
+ final Binder dreamToken;
+ final String dreamPackageName;
+ synchronized (mLock) {
+ if (mCurrentDream == null) {
+ Slog.e(TAG, "Attempt to start DreamActivity, but the device is not "
+ + "dreaming. Aborting without starting the DreamActivity.");
+ return;
+ }
+ dreamToken = mCurrentDream.token;
+ dreamPackageName = mCurrentDream.name.getPackageName();
+ }
+
+ if (!canLaunchDreamActivity(dreamPackageName, intent.getPackage(),
+ callingUid)) {
+ Slog.e(TAG, "The dream activity can be started only when the device is dreaming"
+ + " and only by the active dream package.");
+ return;
+ }
+
+ final IAppTask appTask = mAtmInternal.startDreamActivity(intent, callingUid,
+ callingPid);
+ if (appTask == null) {
+ Slog.e(TAG, "Could not start dream activity.");
+ stopDreamInternal(true, "DreamActivity not started");
+ return;
+ }
+ mController.setDreamAppTask(dreamToken, appTask);
+ });
+ }
+
+ boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
+ int callingUid) {
+ if (dreamPackageName == null || packageName == null) {
+ Slog.e(TAG, "Cannot launch dream activity due to invalid state. dream component= "
+ + dreamPackageName + ", packageName=" + packageName);
+ return false;
+ }
+ if (!mPmInternal.isSameApp(packageName, callingUid, UserHandle.getUserId(callingUid))) {
+ Slog.e(TAG, "Cannot launch dream activity because package="
+ + packageName + " does not match callingUid=" + callingUid);
+ return false;
+ }
+ if (packageName.equals(dreamPackageName)) {
+ return true;
+ }
+ Slog.e(TAG, "Dream packageName does not match active dream. Package " + packageName
+ + " does not match " + dreamPackageName);
+ return false;
+ }
+
}
private final class LocalService extends DreamManagerInternal {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 8cdef8952333..d3371b187074 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -269,6 +269,9 @@ public final class FontManagerService extends IFontManager.Stub {
synchronized (mUpdatableFontDirLock) {
mUpdatableFontDir = createUpdatableFontDir();
if (mUpdatableFontDir == null) {
+ // If fs-verity is not supported, load preinstalled system font map and use it for
+ // all apps.
+ Typeface.loadPreinstalledSystemFontMap();
setSerializedFontMap(serializeSystemServerFontMap());
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7d0d5a73f093..b5e819511ed4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -16,6 +16,8 @@
package com.android.server.hdmi;
+import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;
+
import android.annotation.CallSuper;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
@@ -61,9 +63,6 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
private static final int MAX_HDMI_ACTIVE_SOURCE_HISTORY = 10;
private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
- // Timeout in millisecond for device clean up (5s).
- // Normal actions timeout is 2s but some of them would have several sequence of timeout.
- private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
// Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
// When it expires, we can assume <User Control Release> is received.
private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
@@ -175,6 +174,14 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
};
/**
+ * A callback interface used by local devices use to indicate that they have finished their part
+ * of the standby process.
+ */
+ interface StandbyCompletedCallback {
+ void onStandbyCompleted();
+ }
+
+ /**
* A callback interface to get notified when all pending action is cleared. It can be called
* when timeout happened.
*/
@@ -1260,8 +1267,14 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
* messages like &lt;Standby&gt;
* @param standbyAction Intent action that drives the standby process, either {@link
* HdmiControlService#STANDBY_SCREEN_OFF} or {@link HdmiControlService#STANDBY_SHUTDOWN}
+ * @param callback callback invoked after the standby process for the local device is completed.
*/
- protected void onStandby(boolean initiatedByCec, int standbyAction) {}
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {}
+
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ onStandby(initiatedByCec, standbyAction, null);
+ }
/**
* Called when the initialization of local devices is complete.
@@ -1422,6 +1435,16 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
}
}
+ @ServiceThreadOnly
+ @VisibleForTesting
+ public void invokeStandbyCompletedCallback(StandbyCompletedCallback callback) {
+ assertRunOnServiceThread();
+ if (callback == null) {
+ return;
+ }
+ callback.onStandbyCompleted();
+ }
+
void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildUserControlPressed(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 184bdd778300..b3aa351d69d7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -21,6 +21,7 @@ import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
@@ -243,7 +244,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
// Invalidate the internal active source record when goes to standby
// This set will also update mIsActiveSource
@@ -256,7 +258,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
isSystemAudioActivated() ? "true" : "false");
}
- terminateSystemAudioMode();
+ terminateSystemAudioMode(callback);
}
@Override
@@ -1092,9 +1094,16 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
protected void terminateSystemAudioMode() {
+ terminateSystemAudioMode(null);
+ }
+
+ // Since this method is not just called during the standby process, the callback should be
+ // generalized in the future.
+ protected void terminateSystemAudioMode(StandbyCompletedCallback callback) {
// remove pending initiation actions
removeAction(SystemAudioInitiationActionFromAvr.class);
if (!isSystemAudioActivated()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
@@ -1102,7 +1111,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// send <Set System Audio Mode> [“Off”]
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index c73a07d98f6a..dc416b24f3f3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -28,7 +28,6 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -238,9 +237,11 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
if (!mService.isCecControlEnabled()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
boolean wasActiveSource = isActiveSource();
@@ -248,12 +249,20 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
if (!wasActiveSource) {
+ invokeStandbyCompletedCallback(callback);
return;
}
+ SendMessageCallback sendMessageCallback = new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ };
if (initiatedByCec) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildInactiveSource(
- getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()));
+ getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()),
+ sendMessageCallback);
return;
}
switch (standbyAction) {
@@ -266,7 +275,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV),
+ sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
mService.sendCecCommand(
@@ -275,19 +285,19 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
getDeviceInfo().getLogicalAddress(),
- Constants.ADDR_AUDIO_SYSTEM));
+ Constants.ADDR_AUDIO_SYSTEM), sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
getDeviceInfo().getLogicalAddress(),
- Constants.ADDR_BROADCAST));
+ Constants.ADDR_BROADCAST), sendMessageCallback);
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildInactiveSource(
getDeviceInfo().getLogicalAddress(),
- mService.getPhysicalAddress()));
+ mService.getPhysicalAddress()), sendMessageCallback);
break;
}
break;
@@ -295,7 +305,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// ACTION_SHUTDOWN is taken as a signal to power off all the devices.
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
+ sendMessageCallback);
break;
}
}
@@ -590,7 +601,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
private class SystemWakeLock implements ActiveWakeLock {
- private final WakeLock mWakeLock;
+ private final WakeLockWrapper mWakeLock;
public SystemWakeLock() {
mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(false);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 70870627b8c7..6cca1bd823d4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -377,6 +377,7 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
int oldPath = getActivePortId() != Constants.INVALID_PORT_ID
+ && getActivePortId() != Constants.CEC_SWITCH_HOME
? mService.portIdToPath(getActivePortId()) : getDeviceInfo().getPhysicalAddress();
setActivePath(oldPath);
if (mSkipRoutingControl) {
@@ -1399,10 +1400,12 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@Override
@ServiceThreadOnly
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
assertRunOnServiceThread();
// Seq #11
if (!mService.isCecControlEnabled()) {
+ invokeStandbyCompletedCallback(callback);
return;
}
boolean sendStandbyOnSleep =
@@ -1412,7 +1415,15 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (!initiatedByCec && sendStandbyOnSleep) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(
- getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ invokeStandbyCompletedCallback(callback);
+ }
+ });
+ } else {
+ invokeStandbyCompletedCallback(callback);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3e3835b3fe4e..91cf50319bca 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -21,8 +21,10 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVIC
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_ENABLED;
+import static android.hardware.hdmi.HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.DISABLED;
@@ -110,6 +112,7 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+import com.android.server.hdmi.HdmiCecLocalDevice.StandbyCompletedCallback;
import libcore.util.EmptyArray;
@@ -224,6 +227,10 @@ public class HdmiControlService extends SystemService {
public @interface WakeReason {
}
+ // Timeout in millisecond for device clean up (5s).
+ // Normal actions timeout is 2s but some of them would have several sequences of timeout.
+ static final int DEVICE_CLEANUP_TIMEOUT = 5000;
+
@VisibleForTesting
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI, "");
@@ -492,6 +499,9 @@ public class HdmiControlService extends SystemService {
private DeviceConfigWrapper mDeviceConfig;
@Nullable
+ private WakeLockWrapper mWakeLock;
+
+ @Nullable
private PowerManagerWrapper mPowerManager;
@Nullable
@@ -3040,7 +3050,7 @@ public class HdmiControlService extends SystemService {
}
String powerControlMode = getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_NONE)) {
+ if (powerControlMode.equals(POWER_CONTROL_MODE_NONE)) {
return false;
}
int hdmiCecEnabled = getHdmiCecConfig().getIntValue(
@@ -3687,6 +3697,9 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
@VisibleForTesting
protected void onStandby(final int standbyAction) {
+ if (shouldAcquireWakeLockOnStandby()) {
+ acquireWakeLock();
+ }
mWakeUpMessageReceived = false;
assertRunOnServiceThread();
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
@@ -3752,7 +3765,8 @@ public class HdmiControlService extends SystemService {
return mMenuLanguage;
}
- private void disableCecLocalDevices(PendingActionClearedCallback callback) {
+ @VisibleForTesting
+ protected void disableCecLocalDevices(PendingActionClearedCallback callback) {
if (mCecController != null) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.disableDevice(mStandbyMessageReceived, callback);
@@ -3780,25 +3794,81 @@ public class HdmiControlService extends SystemService {
* cleared during standby. In this case, it does not execute the standby flow.
*/
@ServiceThreadOnly
- private void onPendingActionsCleared(int standbyAction) {
+ @VisibleForTesting
+ protected void onPendingActionsCleared(int standbyAction) {
assertRunOnServiceThread();
Slog.v(TAG, "onPendingActionsCleared");
+ int localDevicesCount = getAllCecLocalDevices().size();
+ final int[] countStandbyCompletedDevices = new int[1];
+ StandbyCompletedCallback callback = new StandbyCompletedCallback() {
+ @Override
+ public void onStandbyCompleted() {
+ if (localDevicesCount < ++countStandbyCompletedDevices[0]) {
+ return;
+ }
+
+ releaseWakeLock();
+ if (isAudioSystemDevice() || !isPowerStandby()) {
+ return;
+ }
+ mCecController.enableSystemCecControl(false);
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ }
+ };
if (mPowerStatusController.isPowerStatusTransientToStandby()) {
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
- device.onStandby(mStandbyMessageReceived, standbyAction);
- }
- if (!isAudioSystemDevice()) {
- mCecController.enableSystemCecControl(false);
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ device.onStandby(mStandbyMessageReceived, standbyAction, callback);
}
}
-
// Always reset this flag to set up for the next standby
mStandbyMessageReceived = false;
}
+ private boolean shouldAcquireWakeLockOnStandby() {
+ boolean sendStandbyOnSleep = false;
+ if (tv() != null) {
+ sendStandbyOnSleep = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
+ == TV_SEND_STANDBY_ON_SLEEP_ENABLED;
+ } else if (playback() != null) {
+ sendStandbyOnSleep = !mHdmiCecConfig.getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)
+ .equals(POWER_CONTROL_MODE_NONE);
+ }
+
+ return isCecControlEnabled() && isPowerOnOrTransient() && sendStandbyOnSleep;
+ }
+
+ /**
+ * Acquire the wake lock used to hold the system awake until the standby process is finished.
+ */
+ @VisibleForTesting
+ protected void acquireWakeLock() {
+ releaseWakeLock();
+ mWakeLock = mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.acquire(DEVICE_CLEANUP_TIMEOUT);
+ }
+
+ /**
+ * Release the wake lock acquired when the standby process started.
+ */
+ @VisibleForTesting
+ protected void releaseWakeLock() {
+ if (mWakeLock != null) {
+ try {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Exception when releasing wake lock.");
+ }
+ mWakeLock = null;
+ }
+ }
+
@VisibleForTesting
void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
@@ -3819,6 +3889,7 @@ public class HdmiControlService extends SystemService {
if (mVendorCommandListenerRecords.isEmpty()) {
return false;
}
+ boolean notifiedVendorCommandToListeners = false;
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
if (hasVendorId) {
int vendorId =
@@ -3831,12 +3902,12 @@ public class HdmiControlService extends SystemService {
}
try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
- return true;
+ notifiedVendorCommandToListeners = true;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify vendor command reception", e);
}
}
- return false;
+ return notifiedVendorCommandToListeners;
}
}
diff --git a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
index f0810687290e..7530b3b239b4 100644
--- a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
@@ -18,7 +18,6 @@ package com.android.server.hdmi;
import android.content.Context;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
/**
* Abstraction around {@link PowerManager} to allow faking PowerManager in tests.
@@ -44,7 +43,54 @@ public class PowerManagerWrapper {
mPowerManager.goToSleep(time, reason, flags);
}
- WakeLock newWakeLock(int levelAndFlags, String tag) {
- return mPowerManager.newWakeLock(levelAndFlags, tag);
+ WakeLockWrapper newWakeLock(int levelAndFlags, String tag) {
+ return new DefaultWakeLockWrapper(mPowerManager.newWakeLock(levelAndFlags, tag));
+ }
+
+ /**
+ * "Default" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Fake" wrapper for
+ * testing - see {@link FakePowerManagerWrapper.FakeWakeLockWrapper}.
+ *
+ * Stores an instance of {@link PowerManager.WakeLock} and directly passes method calls to that
+ * instance.
+ */
+ public static class DefaultWakeLockWrapper implements WakeLockWrapper {
+
+ private static final String TAG = "DefaultWakeLockWrapper";
+
+ private final PowerManager.WakeLock mWakeLock;
+
+ private DefaultWakeLockWrapper(PowerManager.WakeLock wakeLock) {
+ mWakeLock = wakeLock;
+ }
+
+ @Override
+ public void acquire(long timeout) {
+ mWakeLock.acquire(timeout);
+ }
+
+ @Override
+ public void acquire() {
+ mWakeLock.acquire();
+ }
+
+ /**
+ * @throws RuntimeException WakeLock can throw this exception if it is not released
+ * successfully.
+ */
+ @Override
+ public void release() throws RuntimeException {
+ mWakeLock.release();
+ }
+
+ @Override
+ public boolean isHeld() {
+ return mWakeLock.isHeld();
+ }
+
+ @Override
+ public void setReferenceCounted(boolean value) {
+ mWakeLock.setReferenceCounted(value);
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/WakeLockWrapper.java b/services/core/java/com/android/server/hdmi/WakeLockWrapper.java
new file mode 100644
index 000000000000..f6899106b589
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/WakeLockWrapper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+/**
+ * Interface with the methods from {@link PowerManager.WakeLock} used by the HDMI control framework.
+ * Allows the class to be faked for the tests.
+ *
+ * See implementations {@link DefaultWakeLockWrapper} and
+ * {@link FakePowerManagerWrapper.FakeWakeLockWrapper}.
+ */
+public interface WakeLockWrapper {
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#acquire(long)};
+ */
+ void acquire(long timeout);
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#acquire()};
+ */
+ void acquire();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#release()};
+ */
+ void release();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#isHeld()};
+ */
+ boolean isHeld();
+
+ /**
+ * Wraps {@link PowerManager.WakeLock#setReferenceCounted(boolean)};
+ */
+ void setReferenceCounted(boolean value);
+}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 232979037d79..3716e1f5cc33 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -47,6 +47,9 @@ class InputSettingsObserver extends ContentObserver {
private final NativeInputManagerService mNative;
private final Map<Uri, Consumer<String /* reason*/>> mObservers;
+ // Cache prevent notifying same KeyRepeatInfo data to native code multiple times.
+ private KeyRepeatInfo mLastKeyRepeatInfoSettingsUpdate;
+
InputSettingsObserver(Context context, Handler handler, InputManagerService service,
NativeInputManagerService nativeIms) {
super(handler);
@@ -195,7 +198,11 @@ class InputSettingsObserver extends ContentObserver {
final int delayMs = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
UserHandle.USER_CURRENT);
- mNative.setKeyRepeatConfiguration(timeoutMs, delayMs);
+ if (mLastKeyRepeatInfoSettingsUpdate == null || !mLastKeyRepeatInfoSettingsUpdate.isEqualTo(
+ timeoutMs, delayMs)) {
+ mNative.setKeyRepeatConfiguration(timeoutMs, delayMs);
+ mLastKeyRepeatInfoSettingsUpdate = new KeyRepeatInfo(timeoutMs, delayMs);
+ }
}
// Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value.
@@ -214,4 +221,19 @@ class InputSettingsObserver extends ContentObserver {
}
mNative.setMaximumObscuringOpacityForTouch(opacity);
}
+
+ private static class KeyRepeatInfo {
+ private final int mKeyRepeatTimeoutMs;
+ private final int mKeyRepeatDelayMs;
+
+ private KeyRepeatInfo(int keyRepeatTimeoutMs, int keyRepeatDelayMs) {
+ this.mKeyRepeatTimeoutMs = keyRepeatTimeoutMs;
+ this.mKeyRepeatDelayMs = keyRepeatDelayMs;
+ }
+
+ public boolean isEqualTo(int keyRepeatTimeoutMs, int keyRepeatDelayMs) {
+ return mKeyRepeatTimeoutMs == keyRepeatTimeoutMs
+ && mKeyRepeatDelayMs == keyRepeatDelayMs;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b0b1d676bc4b..ba9e280be49d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -352,7 +352,6 @@ final class InputMethodBindingController {
clearCurMethodAndSessions();
mService.clearInputShownLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
- mService.resetSystemUiLocked();
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4dbd82065a66..1ab83f7c5fe5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -48,6 +48,7 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeStateVisibleAllowed;
@@ -633,9 +634,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private InputMethodSubtype mCurrentSubtype;
/**
- * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}.
+ * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
*/
- @GuardedBy("ImfLock.class")
private boolean mCurPerceptible;
/**
@@ -749,26 +749,33 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>();
/**
- * {@code true} if the device is currently interactive with the user, initially true.
- *
- * @see #handleSetInteractive
+ * True if the device is currently interactive with user. The value is true initially.
*/
- @GuardedBy("ImfLock.class")
boolean mIsInteractive = true;
- @GuardedBy("ImfLock.class")
- @InputMethodService.BackDispositionMode
int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
/**
- * The {@link InputMethodService.ImeWindowVisibility} of the currently bound IME,
- * or {@code 0} if no IME is bound.
+ * A set of status bits regarding the active IME.
*
- * <p><em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
+ * <p>This value is a combination of following two bits:</p>
+ * <dl>
+ * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
+ * <dd>
+ * If this bit is ON, connected IME is ready to accept touch/key events.
+ * </dd>
+ * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
+ * <dd>
+ * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
+ * </dd>
+ * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
+ * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
+ * currently invisible.
+ * </dd>
+ * </dl>
+ * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
* {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
*/
- @GuardedBy("ImfLock.class")
- @InputMethodService.ImeWindowVisibility
int mImeWindowVis;
private LocaleList mLastSystemLocales;
@@ -1529,6 +1536,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// Uh oh, current input method is no longer around!
// Pick another one...
Slog.i(TAG, "Current input method removed: " + curInputMethodId);
+ updateSystemUiLocked(0 /* vis */, mBackDisposition);
if (!chooseNewDefaultIMELocked()) {
changed = true;
curIm = null;
@@ -2360,6 +2368,28 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ /**
+ * Called when {@link #resetCurrentMethodAndClientLocked(int)} invoked for clean-up states
+ * before unbinding the current method.
+ */
+ @GuardedBy("ImfLock.class")
+ void onUnbindCurrentMethodByReset() {
+ final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
+ mCurFocusedWindow);
+ if (winState != null && !winState.isRequestedImeVisible()
+ && !mVisibilityStateComputer.isInputShown()) {
+ // Normally, the focus window will apply the IME visibility state to
+ // WindowManager when the IME has applied it. But it would be too late when
+ // switching IMEs in between different users. (Since the focused IME will
+ // first unbind the service to switch to bind the next user of the IME
+ // service, that wouldn't make the attached IME token validity check in time)
+ // As a result, we have to notify WM to apply IME visibility before clearing the
+ // binding states in the first place.
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken,
+ STATE_HIDE_IME);
+ }
+ }
+
/** {@code true} when a {@link ClientState} has attached from starting the input connection. */
@GuardedBy("ImfLock.class")
boolean hasAttachedClient() {
@@ -2831,6 +2861,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
setSelectedMethodIdLocked(null);
+ // Callback before clean-up binding states.
+ onUnbindCurrentMethodByReset();
mBindingController.unbindCurrentMethod();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2931,6 +2963,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
sessionState.mSession.finishSession();
} catch (RemoteException e) {
Slog.w(TAG, "Session failed to close due to remote exception", e);
+ updateSystemUiLocked(0 /* vis */, mBackDisposition);
}
sessionState.mSession = null;
}
@@ -3040,8 +3073,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private boolean shouldShowImeSwitcherLocked(
- @InputMethodService.ImeWindowVisibility int visibility) {
+ private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
// When the IME switcher dialog is shown, the IME switcher button should be hidden.
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -3053,7 +3085,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
&& mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId())) {
return false;
}
- if ((visibility & InputMethodService.IME_ACTIVE) == 0) {
+ if ((visibility & InputMethodService.IME_ACTIVE) == 0
+ || (visibility & InputMethodService.IME_INVISIBLE) != 0) {
return false;
}
if (mWindowManagerInternal.isHardKeyboardAvailable()) {
@@ -3112,9 +3145,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@SuppressWarnings("deprecation")
- private void setImeWindowStatus(@NonNull IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
synchronized (ImfLock.class) {
@@ -3131,7 +3162,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked(vis, backDisposition);
}
final boolean dismissImeOnBackKeyPressed;
@@ -3166,46 +3197,37 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private void updateImeWindowStatus(boolean disableImeIcon) {
synchronized (ImfLock.class) {
- // TODO(b/285109020): disableImeIcon should be stored in a property like
- // mIsSwitcherIconDisabled, but it is currently not reliably cleared.
- updateSystemUiLocked(disableImeIcon ? 0 : mImeWindowVis, mBackDisposition);
+ if (disableImeIcon) {
+ updateSystemUiLocked(0, mBackDisposition);
+ } else {
+ updateSystemUiLocked();
+ }
}
}
@GuardedBy("ImfLock.class")
void updateSystemUiLocked() {
- // This is only used by InputMethodMenuController to trigger the IME switcher icon
- // visibility, by having {@code shouldShowImeSwitcherLocked} called, which depends on the
- // visibility of the IME switcher dialog.
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
// Caution! This method is called in this class. Handle multi-user carefully
@GuardedBy("ImfLock.class")
- private void updateSystemUiLocked(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ private void updateSystemUiLocked(int vis, int backDisposition) {
if (getCurTokenLocked() == null) {
return;
}
if (DEBUG) {
Slog.d(TAG, "IME window vis: " + vis
- + " active: " + ((vis & InputMethodService.IME_ACTIVE) != 0)
- + " visible: " + ((vis & InputMethodService.IME_VISIBLE) != 0)
- + " backDisposition: " + backDisposition
- + " isInteractive: " + mIsInteractive
- + " curPerceptible: " + mCurPerceptible
+ + " active: " + (vis & InputMethodService.IME_ACTIVE)
+ + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
+ " displayId: " + mCurTokenDisplayId);
}
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
- // all updateSystemUi happens on system privilege.
+ // all updateSystemUi happens on system privilege.
final long ident = Binder.clearCallingIdentity();
try {
- if (!mIsInteractive) {
- // When we are not interactive,
- // the visibility should be 0 (no IME icons should be shown).
- vis = 0;
- } else if (!mCurPerceptible) {
+ if (!mCurPerceptible) {
if ((vis & InputMethodService.IME_VISIBLE) != 0) {
vis &= ~InputMethodService.IME_VISIBLE;
vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
@@ -3540,7 +3562,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
mCurPerceptible = perceptible;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked();
}
});
}
@@ -5102,11 +5124,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private void handleSetInteractive(final boolean interactive) {
synchronized (ImfLock.class) {
- if (mIsInteractive == interactive) {
- return;
- }
mIsInteractive = interactive;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
// Inform the current client of the change in active status
if (mCurClient == null || mCurClient.mClient == null) {
@@ -6741,8 +6760,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ public void setImeWindowStatusAsync(int vis, int backDisposition) {
mImms.setImeWindowStatus(mToken, vis, backDisposition);
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 6ba75850f95d..d700c6adfebb 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -36,6 +36,7 @@ import android.hardware.weaver.IWeaver;
import android.hardware.weaver.WeaverConfig;
import android.hardware.weaver.WeaverReadResponse;
import android.hardware.weaver.WeaverReadStatus;
+import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -505,7 +506,7 @@ class SyntheticPasswordManager {
private final Context mContext;
private LockSettingsStorage mStorage;
- private IWeaver mWeaver;
+ private volatile IWeaver mWeaver;
private WeaverConfig mWeaverConfig;
private PasswordSlotManager mPasswordSlotManager;
@@ -536,13 +537,33 @@ class SyntheticPasswordManager {
}
}
- private IWeaver getWeaverService() {
+ private class WeaverDiedRecipient implements IBinder.DeathRecipient {
+ // Not synchronized on the outer class, since setting the pointer to null is atomic, and we
+ // don't want to have to worry about any sort of deadlock here.
+ @Override
+ public void binderDied() {
+ // Weaver died. Try to recover by setting mWeaver to null, which makes
+ // getWeaverService() look up the service again. This is done only as a simple
+ // robustness measure; it should not be relied on. If this triggers, the root cause is
+ // almost certainly a bug in the device's Weaver implementation, which must be fixed.
+ Slog.wtf(TAG, "Weaver service has died");
+ mWeaver.asBinder().unlinkToDeath(this, 0);
+ mWeaver = null;
+ }
+ }
+
+ private @Nullable IWeaver getWeaverServiceInternal() {
// Try to get the AIDL service first
try {
IWeaver aidlWeaver = IWeaver.Stub.asInterface(
ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
if (aidlWeaver != null) {
Slog.i(TAG, "Using AIDL weaver service");
+ try {
+ aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to register Weaver death recipient", e);
+ }
return aidlWeaver;
}
} catch (SecurityException e) {
@@ -568,15 +589,20 @@ class SyntheticPasswordManager {
return LockPatternUtils.isAutoPinConfirmFeatureAvailable();
}
- private synchronized boolean isWeaverAvailable() {
- if (mWeaver != null) {
- return true;
+ /**
+ * Returns a handle to the Weaver service, or null if Weaver is unavailable. Note that not all
+ * devices support Weaver.
+ */
+ private synchronized @Nullable IWeaver getWeaverService() {
+ IWeaver weaver = mWeaver;
+ if (weaver != null) {
+ return weaver;
}
// Re-initialize weaver in case there was a transient error preventing access to it.
- IWeaver weaver = getWeaverService();
+ weaver = getWeaverServiceInternal();
if (weaver == null) {
- return false;
+ return null;
}
final WeaverConfig weaverConfig;
@@ -584,19 +610,18 @@ class SyntheticPasswordManager {
weaverConfig = weaver.getConfig();
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Failed to get weaver config", e);
- return false;
+ return null;
}
if (weaverConfig == null || weaverConfig.slots <= 0) {
Slog.e(TAG, "Invalid weaver config");
- return false;
+ return null;
}
mWeaver = weaver;
mWeaverConfig = weaverConfig;
mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
Slog.i(TAG, "Weaver service initialized");
-
- return true;
+ return weaver;
}
/**
@@ -606,7 +631,7 @@ class SyntheticPasswordManager {
*
* @return the value stored in the weaver slot, or null if the operation fails
*/
- private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
+ private byte[] weaverEnroll(IWeaver weaver, int slot, byte[] key, @Nullable byte[] value) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
throw new IllegalArgumentException("Invalid slot for weaver");
}
@@ -619,7 +644,7 @@ class SyntheticPasswordManager {
value = SecureRandomUtils.randomBytes(mWeaverConfig.valueSize);
}
try {
- mWeaver.write(slot, key, value);
+ weaver.write(slot, key, value);
} catch (RemoteException e) {
Slog.e(TAG, "weaver write binder call failed, slot: " + slot, e);
return null;
@@ -648,7 +673,7 @@ class SyntheticPasswordManager {
* the verification is successful, throttled or failed. If successful, the bound secret
* is also returned.
*/
- private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
+ private VerifyCredentialResponse weaverVerify(IWeaver weaver, int slot, byte[] key) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
throw new IllegalArgumentException("Invalid slot for weaver");
}
@@ -659,7 +684,7 @@ class SyntheticPasswordManager {
}
final WeaverReadResponse readResponse;
try {
- readResponse = mWeaver.read(slot, key);
+ readResponse = weaver.read(slot, key);
} catch (RemoteException e) {
Slog.e(TAG, "weaver read failed, slot: " + slot, e);
return VerifyCredentialResponse.ERROR;
@@ -870,14 +895,15 @@ class SyntheticPasswordManager {
int slot = loadWeaverSlot(protectorId, userId);
destroyState(WEAVER_SLOT_NAME, protectorId, userId);
if (slot != INVALID_WEAVER_SLOT) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Cannot erase Weaver slot because Weaver is unavailable");
return;
}
Set<Integer> usedSlots = getUsedWeaverSlots();
if (!usedSlots.contains(slot)) {
Slogf.i(TAG, "Erasing Weaver slot %d", slot);
- weaverEnroll(slot, null, null);
+ weaverEnroll(weaver, slot, null, null);
mPasswordSlotManager.markSlotDeleted(slot);
} else {
Slogf.i(TAG, "Weaver slot %d was already reused; not erasing it", slot);
@@ -955,13 +981,14 @@ class SyntheticPasswordManager {
Slogf.i(TAG, "Creating LSKF-based protector %016x for user %d", protectorId, userId);
- if (isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver != null) {
// Weaver is available, so make the protector use it to verify the LSKF. Do this even
// if the LSKF is empty, as that gives us support for securely deleting the protector.
int weaverSlot = getNextAvailableWeaverSlot();
Slogf.i(TAG, "Enrolling LSKF for user %d into Weaver slot %d", userId, weaverSlot);
- byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
- null);
+ byte[] weaverSecret = weaverEnroll(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf), null);
if (weaverSecret == null) {
throw new IllegalStateException(
"Fail to enroll user password under weaver " + userId);
@@ -1048,7 +1075,8 @@ class SyntheticPasswordManager {
}
return VerifyCredentialResponse.fromGateKeeperResponse(response);
} else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "No weaver service to verify SP-based persistent data credential");
return VerifyCredentialResponse.ERROR;
}
@@ -1056,7 +1084,8 @@ class SyntheticPasswordManager {
byte[] stretchedLskf = stretchLskf(userCredential, pwd);
int weaverSlot = persistentData.userId;
- return weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
+ return weaverVerify(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
} else {
Slog.e(TAG, "persistentData.type must be TYPE_SP_GATEKEEPER or TYPE_SP_WEAVER, but is "
+ persistentData.type);
@@ -1209,7 +1238,7 @@ class SyntheticPasswordManager {
TokenData tokenData = new TokenData();
tokenData.mType = type;
final byte[] secdiscardable = SecureRandomUtils.randomBytes(SECDISCARDABLE_LENGTH);
- if (isWeaverAvailable()) {
+ if (getWeaverService() != null) {
tokenData.weaverSecret = SecureRandomUtils.randomBytes(mWeaverConfig.valueSize);
tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
PERSONALIZATION_WEAVER_TOKEN, secdiscardable);
@@ -1252,10 +1281,11 @@ class SyntheticPasswordManager {
return false;
}
Slogf.i(TAG, "Creating token-based protector %016x for user %d", tokenHandle, userId);
- if (isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver != null) {
int slot = getNextAvailableWeaverSlot();
Slogf.i(TAG, "Using Weaver slot %d for new token-based protector", slot);
- if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
+ if (weaverEnroll(weaver, slot, null, tokenData.weaverSecret) == null) {
Slog.e(TAG, "Failed to enroll weaver secret when activating token");
return false;
}
@@ -1344,12 +1374,14 @@ class SyntheticPasswordManager {
int weaverSlot = loadWeaverSlot(protectorId, userId);
if (weaverSlot != INVALID_WEAVER_SLOT) {
// Protector uses Weaver to verify the LSKF
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- result.gkResponse = weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf));
+ result.gkResponse = weaverVerify(weaver, weaverSlot,
+ stretchedLskfToWeaverKey(stretchedLskf));
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
@@ -1517,12 +1549,13 @@ class SyntheticPasswordManager {
}
int slotId = loadWeaverSlot(protectorId, userId);
if (slotId != INVALID_WEAVER_SLOT) {
- if (!isWeaverAvailable()) {
+ final IWeaver weaver = getWeaverService();
+ if (weaver == null) {
Slog.e(TAG, "Protector uses Weaver, but Weaver is unavailable");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- VerifyCredentialResponse response = weaverVerify(slotId, null);
+ VerifyCredentialResponse response = weaverVerify(weaver, slotId, null);
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
response.getGatekeeperHAT() == null) {
Slog.e(TAG,
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 82cc53dd3535..63dc59c125a3 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -482,20 +482,20 @@ class MediaRouter2ServiceImpl {
}
public void registerManager(@NonNull IMediaRouter2Manager manager,
- @NonNull String packageName) {
+ @NonNull String callerPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
- if (TextUtils.isEmpty(packageName)) {
- throw new IllegalArgumentException("packageName must not be empty");
+ if (TextUtils.isEmpty(callerPackageName)) {
+ throw new IllegalArgumentException("callerPackageName must not be empty");
}
- final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
- final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerManagerLocked(manager, uid, pid, packageName, userId);
+ registerManagerLocked(manager, callerUid, callerPid, callerPackageName, userId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -673,30 +673,35 @@ class MediaRouter2ServiceImpl {
final long token = Binder.clearCallingIdentity();
try {
- RoutingSessionInfo systemSessionInfo = null;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
List<RoutingSessionInfo> sessionInfos;
if (hasModifyAudioRoutingPermission) {
if (setDeviceRouteSelected) {
- systemSessionInfo = userRecord.mHandler.mSystemProvider
+ // Return a fake system session that shows the device route as selected and
+ // available bluetooth routes as transferable.
+ return userRecord.mHandler.mSystemProvider
.generateDeviceRouteSelectedSessionInfo(packageName);
} else {
sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
if (sessionInfos != null && !sessionInfos.isEmpty()) {
- systemSessionInfo = new RoutingSessionInfo.Builder(sessionInfos.get(0))
- .setClientPackageName(packageName).build();
+ // Return a copy of the current system session with no modification,
+ // except setting the client package name.
+ return new RoutingSessionInfo.Builder(sessionInfos.get(0))
+ .setClientPackageName(packageName)
+ .build();
} else {
Slog.w(TAG, "System provider does not have any session info.");
}
}
} else {
- systemSessionInfo = new RoutingSessionInfo.Builder(
- userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
- .setClientPackageName(packageName).build();
+ return new RoutingSessionInfo.Builder(
+ userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
+ .setClientPackageName(packageName)
+ .build();
}
}
- return systemSessionInfo;
+ return null;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -920,6 +925,17 @@ class MediaRouter2ServiceImpl {
return;
}
+ Slog.i(
+ TAG,
+ TextUtils.formatSimple(
+ "requestCreateSessionWithRouter2 | router: %s(id: %d), old session id: %s,"
+ + " new session's route id: %s, request id: %d",
+ routerRecord.mPackageName,
+ routerRecord.mRouterId,
+ oldSession.getId(),
+ route.getId(),
+ requestId));
+
if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
ManagerRecord manager = routerRecord.mUserRecord.mHandler.findManagerWithId(
toRequesterId(managerRequestId));
@@ -1125,25 +1141,26 @@ class MediaRouter2ServiceImpl {
@GuardedBy("mLock")
private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
- int uid, int pid, @NonNull String packageName, int userId) {
+ int callerUid, int callerPid, @NonNull String callerPackageName, int userId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord != null) {
- Slog.w(TAG, "registerManagerLocked: Same manager already exists. packageName="
- + packageName);
+ Slog.w(TAG, "registerManagerLocked: Same manager already exists. callerPackageName="
+ + callerPackageName);
return;
}
Slog.i(TAG, TextUtils.formatSimple(
- "registerManager | uid: %d, pid: %d, package: %s, user: %d",
- uid, pid, packageName, userId));
+ "registerManager | callerUid: %d, callerPid: %d, package: %s, user: %d",
+ callerUid, callerPid, callerPackageName, userId));
- mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid,
+ mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
"Must hold MEDIA_CONTENT_CONTROL permission.");
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName);
+ managerRecord = new ManagerRecord(
+ userRecord, manager, callerUid, callerPid, callerPackageName);
try {
binder.linkToDeath(managerRecord, 0);
} catch (RemoteException ex) {
@@ -1190,7 +1207,7 @@ class MediaRouter2ServiceImpl {
Slog.i(TAG, TextUtils.formatSimple(
"unregisterManager | package: %s, user: %d, manager: %d",
- managerRecord.mPackageName,
+ managerRecord.mOwnerPackageName,
userRecord.mUserId,
managerRecord.mManagerId));
@@ -1708,20 +1725,20 @@ class MediaRouter2ServiceImpl {
final class ManagerRecord implements IBinder.DeathRecipient {
public final UserRecord mUserRecord;
public final IMediaRouter2Manager mManager;
- public final int mUid;
- public final int mPid;
- public final String mPackageName;
+ public final int mOwnerUid;
+ public final int mOwnerPid;
+ public final String mOwnerPackageName;
public final int mManagerId;
public SessionCreationRequest mLastSessionCreationRequest;
public boolean mIsScanning;
ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager,
- int uid, int pid, String packageName) {
+ int ownerUid, int ownerPid, String ownerPackageName) {
mUserRecord = userRecord;
mManager = manager;
- mUid = uid;
- mPid = pid;
- mPackageName = packageName;
+ mOwnerUid = ownerUid;
+ mOwnerPid = ownerPid;
+ mOwnerPackageName = ownerPackageName;
mManagerId = mNextRouterOrManagerId.getAndIncrement();
}
@@ -1739,10 +1756,10 @@ class MediaRouter2ServiceImpl {
String indent = prefix + " ";
- pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName);
pw.println(indent + "mManagerId=" + mManagerId);
- pw.println(indent + "mUid=" + mUid);
- pw.println(indent + "mPid=" + mPid);
+ pw.println(indent + "mOwnerUid=" + mOwnerUid);
+ pw.println(indent + "mOwnerPid=" + mOwnerPid);
pw.println(indent + "mIsScanning=" + mIsScanning);
if (mLastSessionCreationRequest != null) {
@@ -1770,7 +1787,7 @@ class MediaRouter2ServiceImpl {
@Override
public String toString() {
- return "Manager " + mPackageName + " (pid " + mPid + ")";
+ return "Manager " + mOwnerPackageName + " (pid " + mOwnerPid + ")";
}
}
@@ -1928,7 +1945,7 @@ class MediaRouter2ServiceImpl {
isUidRelevant =
mUserRecord.mRouterRecords.stream().anyMatch(router -> router.mUid == uid)
| mUserRecord.mManagerRecords.stream()
- .anyMatch(manager -> manager.mUid == uid);
+ .anyMatch(manager -> manager.mOwnerUid == uid);
}
if (isUidRelevant) {
sendMessage(PooledLambda.obtainMessage(
@@ -2741,7 +2758,7 @@ class MediaRouter2ServiceImpl {
if (service.mPowerManager.isInteractive()) {
isManagerScanning = managerRecords.stream().anyMatch(manager ->
manager.mIsScanning && service.mActivityManager
- .getPackageImportance(manager.mPackageName)
+ .getPackageImportance(manager.mOwnerPackageName)
<= sPackageImportanceForScanning);
if (isManagerScanning) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index b440e8815c16..cc261a4b9797 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -195,7 +195,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public void registerClientAsUser(IMediaRouterClient client, String packageName, int userId) {
+ public void registerClientAsUser(
+ IMediaRouterClient client, @NonNull String packageName, int userId) {
final int uid = Binder.getCallingUid();
if (!validatePackageName(uid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -537,12 +538,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public void registerManager(IMediaRouter2Manager manager, String packageName) {
+ public void registerManager(IMediaRouter2Manager manager, String callerPackageName) {
final int uid = Binder.getCallingUid();
- if (!validatePackageName(uid, packageName)) {
- throw new SecurityException("packageName must match the calling uid");
+ if (!validatePackageName(uid, callerPackageName)) {
+ throw new SecurityException("callerPackageName must match the calling uid");
}
- mService2.registerManager(manager, packageName);
+ mService2.registerManager(manager, callerPackageName);
}
// Binder call
@@ -693,8 +694,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub
}
@GuardedBy("mLock")
- private void registerClientLocked(IMediaRouterClient client,
- int uid, int pid, String packageName, int userId, boolean trusted) {
+ private void registerClientLocked(
+ IMediaRouterClient client,
+ int uid,
+ int pid,
+ @NonNull String packageName,
+ int userId,
+ boolean trusted) {
final IBinder binder = client.asBinder();
ClientRecord clientRecord = mAllClientRecords.get(binder);
if (clientRecord == null) {
@@ -926,6 +932,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub
clientRecord.dispose();
}
+ /**
+ * Validates whether the provided package name matches a given uid. Returns false if the package
+ * name is null.
+ */
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
@@ -974,8 +984,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub
public String mSelectedRouteId;
public String mGroupId;
- public ClientRecord(UserRecord userRecord, IMediaRouterClient client,
- int uid, int pid, String packageName, boolean trusted) {
+ ClientRecord(
+ UserRecord userRecord,
+ IMediaRouterClient client,
+ int uid,
+ int pid,
+ @NonNull String packageName,
+ boolean trusted) {
mUserRecord = userRecord;
mClient = client;
mUid = uid;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9185a00da570..95ca08cc7fe9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -106,6 +106,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L;
+ /**
+ * {@link MediaSession#setMediaButtonReceiver(PendingIntent)} throws an {@link
+ * IllegalArgumentException} if the provided {@link PendingIntent} targets an {@link
+ * android.app.Activity activity} for apps targeting Android V and above. For apps targeting
+ * Android U and below, the request will be ignored.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long THROW_FOR_ACTIVITY_MEDIA_BUTTON_RECEIVER = 272737196L;
+
private static final String TAG = "MediaSessionRecord";
private static final String[] ART_URIS = new String[] {
MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
@@ -1055,13 +1065,26 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+ public void setMediaButtonReceiver(@Nullable PendingIntent pi) throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
}
+
+ if (pi != null && pi.isActivity()) {
+ if (CompatChanges.isChangeEnabled(
+ THROW_FOR_ACTIVITY_MEDIA_BUTTON_RECEIVER, uid)) {
+ throw new IllegalArgumentException(
+ "The media button receiver cannot be set to an activity.");
+ } else {
+ Log.w(TAG, "Ignoring invalid media button receiver targeting an activity.");
+ return;
+ }
+ }
+
mMediaButtonReceiverHolder =
MediaButtonReceiverHolder.create(mUserId, pi, mPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index f4e6abd47b44..6c9aa4b0d849 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -17,6 +17,7 @@
package com.android.server.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -253,6 +254,16 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
return mDefaultSessionInfo;
}
+ /**
+ * Builds a system {@link RoutingSessionInfo} with the selected route set to the currently
+ * selected <b>device</b> route (wired or built-in, but not bluetooth) and transferable routes
+ * set to the currently available (connected) bluetooth routes.
+ *
+ * <p>The session's client package name is set to the provided package name.
+ *
+ * <p>Returns {@code null} if there are no registered system sessions.
+ */
+ @Nullable
public RoutingSessionInfo generateDeviceRouteSelectedSessionInfo(String packageName) {
synchronized (mLock) {
if (mSessionInfos.isEmpty()) {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index fc506b606633..c649164562d3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -147,7 +147,7 @@ public final class MediaProjectionManagerService extends SystemService
mInjector = injector;
mClock = injector.createClock();
mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
- mCallbackDelegate = new CallbackDelegate();
+ mCallbackDelegate = new CallbackDelegate(injector.createCallbackLooper());
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -182,6 +182,11 @@ public final class MediaProjectionManagerService extends SystemService
Clock createClock() {
return SystemClock::uptimeMillis;
}
+
+ /** Creates the {@link Looper} to be used when notifying callbacks. */
+ Looper createCallbackLooper() {
+ return Looper.getMainLooper();
+ }
}
@Override
@@ -268,7 +273,8 @@ public final class MediaProjectionManagerService extends SystemService
dispatchStop(projection);
}
- private void addCallback(final IMediaProjectionWatcherCallback callback) {
+ @VisibleForTesting
+ void addCallback(final IMediaProjectionWatcherCallback callback) {
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -315,6 +321,12 @@ public final class MediaProjectionManagerService extends SystemService
mCallbackDelegate.dispatchStop(projection);
}
+ private void dispatchSessionSet(
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallbackDelegate.dispatchSession(projectionInfo, session);
+ }
+
/**
* Returns {@code true} when updating the current mirroring session on WM succeeded, and
* {@code false} otherwise.
@@ -335,6 +347,7 @@ public final class MediaProjectionManagerService extends SystemService
if (mProjectionGrant != null) {
// Cache the session details.
mProjectionGrant.mSession = incomingSession;
+ dispatchSessionSet(mProjectionGrant.getProjectionInfo(), incomingSession);
}
return true;
}
@@ -1122,8 +1135,8 @@ public final class MediaProjectionManagerService extends SystemService
private Handler mHandler;
private final Object mLock = new Object();
- public CallbackDelegate() {
- mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/);
+ CallbackDelegate(Looper callbackLooper) {
+ mHandler = new Handler(callbackLooper, null, true /*async*/);
mClientCallbacks = new ArrayMap<IBinder, IMediaProjectionCallback>();
mWatcherCallbacks = new ArrayMap<IBinder, IMediaProjectionWatcherCallback>();
}
@@ -1186,6 +1199,16 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ public void dispatchSession(
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ synchronized (mLock) {
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ mHandler.post(new WatcherSessionCallback(callback, projectionInfo, session));
+ }
+ }
+ }
+
public void dispatchResize(MediaProjection projection, int width, int height) {
if (projection == null) {
Slog.e(TAG,
@@ -1302,6 +1325,29 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ private static final class WatcherSessionCallback implements Runnable {
+ private final IMediaProjectionWatcherCallback mCallback;
+ private final MediaProjectionInfo mProjectionInfo;
+ private final ContentRecordingSession mSession;
+
+ WatcherSessionCallback(
+ @NonNull IMediaProjectionWatcherCallback callback,
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallback = callback;
+ mProjectionInfo = projectionInfo;
+ mSession = session;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCallback.onRecordingSessionSet(mProjectionInfo, mSession);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify content recording session changed", e);
+ }
+ }
+ }
private static String typeToString(int type) {
switch (type) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f571c9d7cc7f..cdc63473cacb 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5592,6 +5592,11 @@ public class NotificationManagerService extends SystemService {
boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
checkNotificationListenerAccess();
+ if (granted && listener.flattenToString().length()
+ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
+ throw new IllegalArgumentException(
+ "Component name too long: " + listener.flattenToString());
+ }
if (!userSet && isNotificationListenerAccessUserSet(listener)) {
// Don't override user's choice
return;
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index a5fa81da3c07..6505e8b91c06 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -834,12 +834,6 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
updateEntireShouldFilterCacheInner(snapshot, settings, usersRef[0], USER_ALL);
- synchronized (mImplicitlyQueryableLock) {
- if (mNeedToUpdateCacheForImplicitAccess) {
- updateShouldFilterCacheForImplicitAccess();
- mNeedToUpdateCacheForImplicitAccess = false;
- }
- }
logCacheRebuilt(reason, SystemClock.currentTimeMicro() - currentTimeUs,
users.length, settings.size());
@@ -850,7 +844,14 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
return;
}
- mCacheReady = true;
+ synchronized (mImplicitlyQueryableLock) {
+ if (mNeedToUpdateCacheForImplicitAccess) {
+ updateShouldFilterCacheForImplicitAccess();
+ mNeedToUpdateCacheForImplicitAccess = false;
+ }
+ mCacheReady = true;
+ }
+
onChanged();
}, delayMs);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 9f3ee1cfd979..4963dd4ecb6a 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -364,6 +365,12 @@ final class DeletePackageHelper {
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
+ if (PackageManagerServiceUtils.isSystemApp(ps)
+ && mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM)
+ == PERMISSION_GRANTED) {
+ Slog.w(TAG, "Attempt to delete keyguard system package " + packageName);
+ return false;
+ }
action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user);
}
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
@@ -625,7 +632,8 @@ final class DeletePackageHelper {
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashScreenTheme*/,
- 0 /*firstInstallTime*/);
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index e01e1d3f3edd..7d4776285db0 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1077,6 +1077,8 @@ final class InstallPackageHelper {
final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
final boolean isRollback =
request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean extractProfile =
+ ((installFlags & PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES) == 0);
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (request.isInstallMove()) {
// moving a complete application; perform an initial scan on the new install location
@@ -1112,7 +1114,9 @@ final class InstallPackageHelper {
@ParsingPackageUtils.ParseFlags final int parseFlags =
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
| ParsingPackageUtils.PARSE_ENFORCE_CODE
- | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
+ | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0)
+ | (extractProfile
+ ? ParsingPackageUtils.PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK : 0);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
@@ -2790,6 +2794,8 @@ final class InstallPackageHelper {
final String[] pkgNames = new String[]{
request.getRemovedInfo().mRemovedPackage};
final int[] uids = new int[]{request.getRemovedInfo().mUid};
+ mPm.notifyResourcesChanged(false /* mediaStatus */,
+ true /* replacing */, pkgNames, uids);
mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
false /* mediaStatus */, true /* replacing */, pkgNames, uids);
}
@@ -2995,6 +3001,8 @@ final class InstallPackageHelper {
final int[] uids = new int[]{request.getPkg().getUid()};
mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
true /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */,
+ pkgNames, uids);
}
} else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib
// No need to kill consumers if it's installation of new version static shared lib.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3377f0090027..af6c1a25967c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -29,6 +29,7 @@ import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -128,6 +129,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
@@ -710,6 +712,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@NonNull
private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper();
+ @NonNull
+ private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+
private final ModuleInfoProvider mModuleInfoProvider;
final ApexManager mApexManager;
@@ -1832,6 +1837,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mStorageEventHelper = testParams.storageEventHelper;
+ mPackageMonitorCallbackHelper = testParams.packageMonitorCallbackHelper;
registerObservers(false);
invalidatePackageInfoCache();
@@ -1972,6 +1978,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
mBroadcastHelper = new BroadcastHelper(mInjector);
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
@@ -2493,13 +2500,22 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@NonNull
private String getRequiredServicesExtensionPackageLPr(@NonNull Computer computer) {
- String servicesExtensionPackage =
- ensureSystemPackageName(computer,
- mContext.getString(R.string.config_servicesExtensionPackage));
+ String configServicesExtensionPackage = mContext.getString(
+ R.string.config_servicesExtensionPackage);
+ if (TextUtils.isEmpty(configServicesExtensionPackage)) {
+ throw new RuntimeException(
+ "Required services extension package failed due to "
+ + "config_servicesExtensionPackage is empty.");
+ }
+ String servicesExtensionPackage = ensureSystemPackageName(computer,
+ configServicesExtensionPackage);
if (TextUtils.isEmpty(servicesExtensionPackage)) {
throw new RuntimeException(
- "Required services extension package is missing, check "
- + "config_servicesExtensionPackage.");
+ "Required services extension package is missing, "
+ + "config_servicesExtensionPackage had defined with "
+ + configServicesExtensionPackage
+ + ", but can not find the package info on the system image, check if "
+ + "the package has a problem.");
}
return servicesExtensionPackage;
}
@@ -2975,6 +2991,25 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags,
targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
null /* filterExtrasForReceiver */, bOptions));
+ if (targetPkg == null) {
+ // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
+ // many times to different targets, e.g. installer app, permission controller, other
+ // registered apps. We should filter it to avoid calling back many times for the same
+ // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
+ // installer app or null for registered apps. The callback only need to send back to the
+ // registered apps so we check the null condition here.
+ notifyPackageMonitor(action, pkg, extras, userIds);
+ }
+ }
+
+ void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds) {
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds);
+ }
+
+ void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
+ @NonNull String[] pkgNames, @NonNull int[] uids) {
+ mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
+ uids);
}
@Override
@@ -3023,6 +3058,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
userIds, snapshot.getPackageStates());
mHandler.post(() -> mBroadcastHelper.sendPackageAddedForNewUsers(
packageName, appId, userIds, instantUserIds, dataLoaderType, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
+ instantUserIds, dataLoaderType);
if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
mHandler.post(() -> {
for (int userId : userIds) {
@@ -4015,6 +4052,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
instantUserIds, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
+ packageUid, reason, userIds);
}
/**
@@ -5100,6 +5139,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
+ @PackageManager.UserMinAspectRatio
+ public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
+ final Computer snapshot = snapshotComputer();
+ final int callingUid = Binder.getCallingUid();
+ snapshot.enforceCrossUserPermission(
+ callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "getUserMinAspectRatio");
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
+ : packageState.getUserStateOrDefault(userId).getMinAspectRatio();
+ }
+
+ @Override
public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshot();
@@ -6064,6 +6117,32 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return true;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
+ @Override
+ public void setUserMinAspectRatio(@NonNull String packageName, int userId,
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ setUserMinAspectRatio_enforcePermission();
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = snapshotComputer();
+ snapshot.enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "setUserMinAspectRatio");
+ enforceOwnerRights(snapshot, packageName, callingUid);
+
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ if (packageState == null) {
+ return;
+ }
+
+ if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) {
+ return;
+ }
+
+ commitPackageStateMutation(null, packageName, state ->
+ state.userState(userId).setMinAspectRatio(aspectRatio));
+ }
+
@Override
@SuppressWarnings("GuardedBy")
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
@@ -6142,6 +6221,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
+ public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId);
+ }
+
+ @Override
+ public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) {
+ mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
+ }
+
+ @Override
public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.TypeMask int optional, @Checksum.TypeMask int required,
@Nullable List trustedInstallers,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 50711deafa58..ca572091486e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -123,4 +123,5 @@ public final class PackageManagerServiceTestParams {
public Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
public boolean shouldStopSystemPackagesByDefault;
public FreeStorageHelper freeStorageHelper;
+ public PackageMonitorCallbackHelper packageMonitorCallbackHelper;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e4692f19ada9..5cb480c0278f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3436,6 +3436,10 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
+ case "--no-profile":
+ sessionParams.installFlags |=
+ PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4324,7 +4328,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
- pw.println(" [--enable-rollback]");
+ pw.println(" [--enable-rollback] [--no-profile]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
pw.println(" [PATH [SPLIT...]|-]");
@@ -4357,6 +4361,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --apex: install an .apex file, not an .apk");
pw.println(" --force-non-staged: force the installation to run under a non-staged");
pw.println(" session, which may complete without requiring a reboot");
+ pw.println(" --no-profile: don't extract the profiles from the apk");
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
+ DEFAULT_STAGED_READY_TIMEOUT_MS);
pw.println(" milliseconds for pre-reboot verification to complete when");
@@ -4378,7 +4383,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
- pw.println(" [--multi-package] [--staged] [--update-ownership]");
+ pw.println(" [--multi-package] [--staged] [--no-profile] [--update-ownership]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
pw.println(" to push data into the session, and \"install-commit\" to finish.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
new file mode 100644
index 000000000000..c582321da1ec
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly
+ * used by PackageMonitor to improve the broadcast latency. */
+class PackageMonitorCallbackHelper {
+ @NonNull
+ private final Object mLock = new Object();
+ final IActivityManager mActivityManager = ActivityManager.getService();
+
+ final Handler mHandler;
+
+ PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) {
+ mHandler = injector.getHandler();
+ }
+
+ @NonNull
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
+
+ public void registerPackageMonitorCallback(IRemoteCallback callback, int userId) {
+ synchronized (mLock) {
+ mCallbacks.register(callback, userId);
+ }
+ }
+
+ public void unregisterPackageMonitorCallback(IRemoteCallback callback) {
+ synchronized (mLock) {
+ mCallbacks.unregister(callback);
+ }
+ }
+
+ public void notifyPackageAddedForNewUsers(String packageName,
+ @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds,
+ int dataLoaderType) {
+ Bundle extras = new Bundle(2);
+ // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
+ final int uid = UserHandle.getUid(
+ (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
+ extras.putInt(Intent.EXTRA_UID, uid);
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+ notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
+ userIds /* userIds */);
+ }
+
+ public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
+ @NonNull String[] pkgNames, @NonNull int[] uids) {
+ Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
+ if (replacing) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
+ }
+ String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+ : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
+ notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */);
+ }
+
+ public void notifyPackageChanged(String packageName, boolean dontKillApp,
+ ArrayList<String> componentNames, int packageUid, String reason, int[] userIds) {
+ Bundle extras = new Bundle(4);
+ extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
+ String[] nameList = new String[componentNames.size()];
+ componentNames.toArray(nameList);
+ extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
+ extras.putInt(Intent.EXTRA_UID, packageUid);
+ if (reason != null) {
+ extras.putString(Intent.EXTRA_REASON, reason);
+ }
+ notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds);
+ }
+
+ public void notifyPackageMonitor(String action, String pkg, Bundle extras,
+ int[] userIds) {
+ if (!isAllowedCallbackAction(action)) {
+ return;
+ }
+ try {
+ final int[] resolvedUserIds;
+ if (userIds == null) {
+ if (mActivityManager == null) return;
+ resolvedUserIds = mActivityManager.getRunningUserIds();
+ } else {
+ resolvedUserIds = userIds;
+ }
+ doNotifyCallbacks(action, pkg, extras, resolvedUserIds);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+
+ private static boolean isAllowedCallbackAction(String action) {
+ return TextUtils.equals(action, Intent.ACTION_PACKAGE_ADDED)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGE_REMOVED)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGE_CHANGED)
+ || TextUtils.equals(action, Intent.ACTION_UID_REMOVED)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGES_SUSPENDED)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGES_UNSUSPENDED)
+ || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)
+ || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ }
+
+ private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds) {
+ RemoteCallbackList<IRemoteCallback> callbacks;
+ synchronized (mLock) {
+ callbacks = mCallbacks;
+ }
+ for (int userId : userIds) {
+ final Intent intent = new Intent(action,
+ pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
+ uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ }
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+
+ mHandler.post(() -> callbacks.broadcast((callback, user) -> {
+ int registerUserId = (int) user;
+ if ((registerUserId != UserHandle.USER_ALL) && (registerUserId != userId)) {
+ return;
+ }
+ invokeCallback(callback, intent);
+ }));
+ }
+ }
+
+ private void invokeCallback(IRemoteCallback callback, Intent intent) {
+ try {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, intent);
+ callback.sendResult(bundle);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 24118203bb56..3e9ccac2993a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -875,7 +875,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
String harmfulAppWarning, String splashScreenTheme,
- long firstInstallTime) {
+ long firstInstallTime, int aspectRatio) {
modifyUserState(userId)
.setSuspendParams(suspendParams)
.setCeDataInode(ceDataInode)
@@ -894,7 +894,8 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
.setVirtualPreload(virtualPreload)
.setHarmfulAppWarning(harmfulAppWarning)
.setSplashScreenTheme(splashScreenTheme)
- .setFirstInstallTimeMillis(firstInstallTime);
+ .setFirstInstallTimeMillis(firstInstallTime)
+ .setMinAspectRatio(aspectRatio);
onChanged();
}
@@ -912,7 +913,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
? null : otherState.getDisabledComponentsNoCopy().untrackedStorage(),
otherState.getInstallReason(), otherState.getUninstallReason(),
otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme(),
- otherState.getFirstInstallTimeMillis());
+ otherState.getFirstInstallTimeMillis(), otherState.getMinAspectRatio());
}
WatchedArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 9ff83929a609..6e273cf8e5d5 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -655,6 +655,7 @@ final class PreferredActivityHelper {
final Iterator<PreferredActivity> it = pir.filterIterator();
while (it.hasNext()) {
final PreferredActivity pa = it.next();
+ if (pa == null) continue;
final String prefPackageName = pa.mPref.mComponent.getPackageName();
if (packageName == null
|| (prefPackageName.equals(packageName) && pa.mPref.mAlways)) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index be031250bc76..87f912653c60 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -352,6 +352,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme";
+ private static final String ATTR_MIN_ASPECT_RATIO = "min-aspect-ratio";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_BUILD_FINGERPRINT = "buildFingerprint";
@@ -1127,7 +1128,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashscreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
}
@@ -1794,7 +1796,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /* splashScreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
return;
@@ -1889,6 +1892,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
ATTR_SPLASH_SCREEN_THEME);
final long firstInstallTime = parser.getAttributeLongHex(null,
ATTR_FIRST_INSTALL_TIME, 0);
+ final int minAspectRatio = parser.getAttributeInt(null,
+ ATTR_MIN_ASPECT_RATIO,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1965,7 +1971,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
enabledCaller, enabledComponents, disabledComponents, installReason,
uninstallReason, harmfulAppWarning, splashScreenTheme,
firstInstallTime != 0 ? firstInstallTime :
- origFirstInstallTimes.getOrDefault(name, 0L));
+ origFirstInstallTimes.getOrDefault(name, 0L),
+ minAspectRatio);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -2265,6 +2272,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME,
ustate.getSplashScreenTheme());
}
+ if (ustate.getMinAspectRatio()
+ != PackageManager.USER_MIN_ASPECT_RATIO_UNSET) {
+ serializer.attributeInt(null, ATTR_MIN_ASPECT_RATIO,
+ ustate.getMinAspectRatio());
+ }
if (ustate.isSuspended()) {
for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5b3514c01f9f..710e0b72ecfb 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import android.Manifest.permission;
@@ -24,6 +25,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.IUriGrantsManager;
@@ -4407,8 +4409,11 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
try {
+ ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_DENIED);
intentSender.sendIntent(mContext, /* code= */ 0, extras,
- /* onFinished=*/ null, /* handler= */ null);
+ /* onFinished=*/ null, /* handler= */ null, null, options.toBundle());
} catch (SendIntentException e) {
Slog.w(TAG, "sendIntent failed().", e);
}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 8f8f4376d2cc..6f0fe63cdaa8 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -306,6 +306,7 @@ public final class StorageEventHelper extends StorageEventListener {
}
mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus,
replacing, packageNames, packageUids);
+ mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
}
/**
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 08934c69e099..89aff9eec4cf 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -633,6 +633,7 @@ public final class SuspendPackageHelper {
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
mPm.snapshotComputer(), callingUid, intentExtras),
options));
+ mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId});
}
/**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4b8be5bce4c6..e9c6511aee9f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1037,7 +1037,7 @@ public class UserManagerService extends IUserManager.Stub {
final UserData userData = mUsers.valueAt(i);
final int userId = userData.info.id;
if (userId != currentUser && userData.info.isFull() && !userData.info.partial
- && !mRemovingUserIds.get(userId)) {
+ && userData.info.isEnabled() && !mRemovingUserIds.get(userId)) {
final long userEnteredTime = userData.mLastEnteredForegroundTimeMillis;
if (userEnteredTime > latestEnteredTime) {
latestEnteredTime = userEnteredTime;
@@ -2987,14 +2987,14 @@ public class UserManagerService extends IUserManager.Stub {
Preconditions.checkState(mCachedEffectiveUserRestrictions.getRestrictions(userId)
!= newBaseRestrictions);
- if (mBaseUserRestrictions.updateRestrictions(userId, newBaseRestrictions)) {
+ if (mBaseUserRestrictions.updateRestrictions(userId, new Bundle(newBaseRestrictions))) {
scheduleWriteUser(userId);
}
}
final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
- mCachedEffectiveUserRestrictions.updateRestrictions(userId, effective);
+ mCachedEffectiveUserRestrictions.updateRestrictions(userId, new Bundle(effective));
// Apply the new restrictions.
if (DBG) {
@@ -5636,8 +5636,14 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- @GuardedBy("mUsersLock")
@VisibleForTesting
+ void addRemovingUserId(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ addRemovingUserIdLocked(userId);
+ }
+ }
+
+ @GuardedBy("mUsersLock")
void addRemovingUserIdLocked(@UserIdInt int userId) {
// We remember deleted user IDs to prevent them from being
// reused during the current boot; they can still be reused
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 9ebb8d6ac4be..7bdcd685a2e9 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -633,4 +633,12 @@ public final class UserTypeDetails {
public boolean isCommunalProfile() {
return UserManager.isUserTypeCommunalProfile(mName);
}
+
+ /**
+ * Returns whether the user type is a private profile
+ * (i.e. {@link UserManager#USER_TYPE_PROFILE_PRIVATE}).
+ */
+ public boolean isPrivateProfile() {
+ return UserManager.isUserTypePrivateProfile(mName);
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1569e06bd06d..f7967c0a60d9 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -35,6 +35,7 @@ import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_COMMUNAL;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.os.UserManager.USER_TYPE_PROFILE_TEST;
import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
@@ -108,6 +109,7 @@ public final class UserTypeFactory {
builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone());
builders.put(USER_TYPE_PROFILE_COMMUNAL, getDefaultTypeProfileCommunal());
+ builders.put(USER_TYPE_PROFILE_PRIVATE, getDefaultTypeProfilePrivate());
if (Build.IS_DEBUGGABLE) {
builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest());
}
@@ -264,6 +266,39 @@ public final class UserTypeFactory {
}
/**
+ * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_PRIVATE}
+ * configuration.
+ */
+ private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() {
+ return new UserTypeDetails.Builder()
+ .setName(USER_TYPE_PROFILE_PRIVATE)
+ .setBaseType(FLAG_PROFILE)
+ .setMaxAllowedPerParent(1)
+ .setLabel(0)
+ .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
+ .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
+ .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
+ .setStatusBarIcon(com.android.internal.R.drawable.ic_test_badge_experiment)
+ .setBadgeLabels(
+ com.android.internal.R.string.managed_profile_label_badge,
+ com.android.internal.R.string.managed_profile_label_badge_2,
+ com.android.internal.R.string.managed_profile_label_badge_3)
+ .setBadgeColors(
+ com.android.internal.R.color.profile_badge_2)
+ .setDarkThemeBadgeColors(
+ com.android.internal.R.color.profile_badge_2_dark)
+ .setDefaultRestrictions(getDefaultProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
+ .setDefaultUserProperties(new UserProperties.Builder()
+ .setStartWithParent(true)
+ .setCredentialShareableWithParent(false)
+ .setMediaSharedWithParent(false)
+ .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
+ .setCrossProfileIntentFilterAccessControl(
+ UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM));
+ }
+
+ /**
* Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
* configuration.
*/
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 9d7354ee2f87..056aae49ae82 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -664,29 +664,39 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Override
public PackageImpl addUsesLibrary(String libraryName) {
- this.usesLibraries = CollectionUtils.add(this.usesLibraries,
- TextUtils.safeIntern(libraryName));
+ libraryName = TextUtils.safeIntern(libraryName);
+ if (!ArrayUtils.contains(this.usesLibraries, libraryName)) {
+ this.usesLibraries = CollectionUtils.add(this.usesLibraries, libraryName);
+ }
return this;
}
@Override
public final PackageImpl addUsesNativeLibrary(String libraryName) {
- this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
- TextUtils.safeIntern(libraryName));
+ libraryName = TextUtils.safeIntern(libraryName);
+ if (!ArrayUtils.contains(this.usesNativeLibraries, libraryName)) {
+ this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries, libraryName);
+ }
return this;
}
@Override
public PackageImpl addUsesOptionalLibrary(String libraryName) {
- this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
- TextUtils.safeIntern(libraryName));
+ libraryName = TextUtils.safeIntern(libraryName);
+ if (!ArrayUtils.contains(this.usesOptionalLibraries, libraryName)) {
+ this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
+ libraryName);
+ }
return this;
}
@Override
public final PackageImpl addUsesOptionalNativeLibrary(String libraryName) {
- this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
- TextUtils.safeIntern(libraryName));
+ libraryName = TextUtils.safeIntern(libraryName);
+ if (!ArrayUtils.contains(this.usesOptionalNativeLibraries, libraryName)) {
+ this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+ libraryName);
+ }
return this;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 91a25db3710e..3d056e89ba78 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -377,6 +377,8 @@ public class PackageStateImpl implements PackageState {
private final int mUninstallReason;
@Nullable
private final String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private final int mMinAspectRatio;
private final long mFirstInstallTimeMillis;
private UserStateImpl(@NonNull PackageUserState userState) {
@@ -392,6 +394,7 @@ public class PackageStateImpl implements PackageState {
mSharedLibraryOverlayPaths = userState.getSharedLibraryOverlayPaths();
mUninstallReason = userState.getUninstallReason();
mSplashScreenTheme = userState.getSplashScreenTheme();
+ mMinAspectRatio = userState.getMinAspectRatio();
setBoolean(Booleans.HIDDEN, userState.isHidden());
setBoolean(Booleans.INSTALLED, userState.isInstalled());
setBoolean(Booleans.INSTANT_APP, userState.isInstantApp());
@@ -543,6 +546,11 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
+ @DataClass.Generated.Member
public long getFirstInstallTimeMillis() {
return mFirstInstallTimeMillis;
}
@@ -554,10 +562,10 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated(
- time = 1671671043891L,
+ time = 1687938966108L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 2048d651e11e..f75d214c5128 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -217,4 +217,12 @@ public interface PackageUserState {
*/
@Nullable
String getSplashScreenTheme();
+
+ /**
+ * @return the min aspect ratio setting of the package which by default is unset
+ * unless it has been set by the user
+ * @hide
+ */
+ @PackageManager.UserMinAspectRatio
+ int getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 73fb67298aec..1fb12a8ce218 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -136,6 +136,11 @@ class PackageUserStateDefault implements PackageUserStateInternal {
}
@Override
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+ }
+
+ @Override
public long getFirstInstallTimeMillis() {
return 0;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index e8e2d4179326..d911ac1cfcd8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -83,6 +83,9 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
@Nullable
private String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private int mMinAspectRatio = PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+
/**
* Suspending package to suspend params
*/
@@ -146,6 +149,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
mHarmfulAppWarning = other.mHarmfulAppWarning;
mLastDisableAppCaller = other.mLastDisableAppCaller;
mSplashScreenTheme = other.mSplashScreenTheme;
+ mMinAspectRatio = other.mMinAspectRatio;
mSuspendParams = other.mSuspendParams == null ? null : other.mSuspendParams.snapshot();
mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null
? null : other.mComponentLabelIconOverrideMap.snapshot();
@@ -508,6 +512,19 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
}
/**
+ * Sets user min aspect ratio override value
+ * @see PackageManager.UserMinAspectRatio
+ */
+ public @NonNull PackageUserStateImpl setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int value) {
+ mMinAspectRatio = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.UserMinAspectRatio.class, null, mMinAspectRatio);
+ onChanged();
+ return this;
+ }
+
+ /**
* Suspending package to suspend params
*/
public @NonNull PackageUserStateImpl setSuspendParams(
@@ -679,6 +696,11 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
return mSplashScreenTheme;
}
+ @DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
/**
* Suspending package to suspend params
*/
@@ -766,6 +788,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
&& Objects.equals(mOverlayPaths, that.mOverlayPaths)
&& Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
&& Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
+ && mMinAspectRatio == that.mMinAspectRatio
&& Objects.equals(mSuspendParams, that.mSuspendParams)
&& Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap)
&& mFirstInstallTimeMillis == that.mFirstInstallTimeMillis
@@ -798,6 +821,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
_hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme);
+ _hash = 31 * _hash + mMinAspectRatio;
_hash = 31 * _hash + Objects.hashCode(mSuspendParams);
_hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
_hash = 31 * _hash + Long.hashCode(mFirstInstallTimeMillis);
@@ -807,10 +831,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt
}
@DataClass.Generated(
- time = 1686952839807L,
+ time = 1687938397579L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 8125b0f662aa..8430cf7a0d11 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -439,6 +439,16 @@ public class PackageStateMutator {
}
return null;
}
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ if (mUserState != null) {
+ mUserState.setMinAspectRatio(aspectRatio);
+ }
+ return this;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index 11d6d97d3920..0c6c6723b79b 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.SuspendParams;
public interface PackageUserStateWrite {
@@ -68,4 +69,8 @@ public interface PackageUserStateWrite {
@NonNull
PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon);
+
+ /** @see PackageUserStateImpl#setMinAspectRatio(int) */
+ @NonNull
+ PackageUserStateWrite setMinAspectRatio(@PackageManager.UserMinAspectRatio int aspectRatio);
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e2cb87e72c7a..d737b1c6bfa6 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -50,6 +50,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
+import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.pm.parsing.PackageLite;
@@ -73,6 +74,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.ext.SdkExtensions;
+import android.os.incremental.IncrementalManager;
import android.permission.PermissionManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -240,6 +242,11 @@ public class ParsingPackageUtils {
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
public static final int PARSE_APK_IN_APEX = 1 << 9;
+ /**
+ * This flag is to determine whether to extract the baseline profiles from the apk or not.
+ */
+ public static final int PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK = 1 << 10;
+
public static final int PARSE_CHATTY = 1 << 31;
/** The total maximum number of activities, services, providers and activity-aliases */
@@ -251,14 +258,16 @@ public class ParsingPackageUtils {
private static final int MAX_PERMISSION_NAME_LENGTH = 512;
@IntDef(flag = true, prefix = { "PARSE_" }, value = {
+ PARSE_APK_IN_APEX,
PARSE_CHATTY,
PARSE_COLLECT_CERTIFICATES,
PARSE_ENFORCE_CODE,
PARSE_EXTERNAL_STORAGE,
+ PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK,
+ PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
PARSE_IGNORE_PROCESSES,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
- PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ParseFlags {}
@@ -559,6 +568,26 @@ public class ParsingPackageUtils {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
+ // 1. The apkFile is an apk file
+ // 2. The flags include PARSE_EXTRACT_PROFILE_FROM_APK
+ // 3. The apk patch is NOT an incremental path
+ // 4. If the .dm file exists in the current apk directory, it means the caller
+ // prepares the .dm file. Don't extract the profiles from the apk again.
+ if (ApkLiteParseUtils.isApkFile(apkFile)
+ && (flags & PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK) != 0
+ && !IncrementalManager.isIncrementalPath(apkPath)
+ && DexMetadataHelper.findDexMetadataForFile(apkFile) == null) {
+ // Extract the baseline profiles from the apk if the profiles exist in the assets
+ // directory in the apk.
+ boolean extractedResult =
+ DexMetadataHelper.extractBaselineProfilesToDexMetadataFileFromApk(assets,
+ apkPath);
+
+ if (DEBUG_JAR) {
+ Slog.d(TAG, "Extract profiles " + (extractedResult ? "success" : "fail"));
+ }
+ }
+
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4957eaf256a9..fc88776b2208 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -724,8 +724,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
finishKeyguardDrawn();
break;
case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
- if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");
- finishWindowsDrawn(msg.arg1);
+ final int displayId = msg.arg1;
+ if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ finishWindowsDrawn(displayId);
break;
case MSG_HIDE_BOOT_MESSAGE:
handleHideBootMessage();
@@ -3711,19 +3714,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
- if (mKeyguardDelegate != null && waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
+ if (mKeyguardDelegate != null) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
- } else {
- setKeyguardOccludedLw(occluded);
}
}
@Override
public int applyKeyguardOcclusionChange() {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
- + mPendingKeyguardOccluded);
+ + mPendingKeyguardOccluded + " changed=" + mKeyguardOccludedChanged);
// TODO(b/276433230): Explicitly save before/after for occlude state in each
// Transition so we don't need to update SysUI every time.
@@ -5137,15 +5138,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
// as well as enabling the orientation change logic/sensor.
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for every display");
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- INVALID_DISPLAY, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, INVALID_DISPLAY, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// Called on the DisplayManager's DisplayPowerController thread.
@@ -5225,15 +5221,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mScreenOnListeners.put(displayId, screenOnListener);
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display: " + displayId);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- displayId, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, displayId);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, displayId, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, displayId);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 887f9461bdce..03a7bd3b68b3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -169,7 +169,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*
* @param occluded Whether Keyguard is currently occluded or not.
*/
- void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
+ void onKeyguardOccludedChangedLw(boolean occluded);
/**
* Commit any queued changes to keyguard occlude status that had been deferred during the
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index a694e315ac29..3ecc9853be91 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -222,7 +222,7 @@ public class Notifier {
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mWakeLockLog = new WakeLockLog();
+ mWakeLockLog = new WakeLockLog(context);
// Initialize interactive state for battery stats.
try {
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index cf1bfc3b555f..fbfe291b1659 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -20,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
@@ -38,7 +39,8 @@
{
"name": "FrameworksServicesTests",
"options": [
- {"include-filter": "com.android.server.power"}
+ {"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"}
]
}
]
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index d20c7f1fe9c6..d3486a4a1cf8 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -16,20 +16,26 @@
package com.android.server.power;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.Iterator;
+import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
/**
* Simple Log for wake lock events. Optimized to reduce memory usage.
@@ -117,7 +123,7 @@ final class WakeLockLog {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
/**
- * Lock protects WakeLockLock.dump (binder thread) from conflicting with changes to the log
+ * Lock protects WakeLockLog.dump (binder thread) from conflicting with changes to the log
* happening on the background thread.
*/
private final Object mLock = new Object();
@@ -126,18 +132,20 @@ final class WakeLockLog {
private final TheLog mLog;
private final TagDatabase mTagDatabase;
private final SimpleDateFormat mDumpsysDateFormat;
+ private final Context mContext;
- WakeLockLog() {
- this(new Injector());
+ WakeLockLog(Context context) {
+ this(new Injector(), context);
}
@VisibleForTesting
- WakeLockLog(Injector injector) {
+ WakeLockLog(Injector injector, Context context) {
mInjector = injector;
mTagDatabase = new TagDatabase(injector);
EntryByteTranslator translator = new EntryByteTranslator(mTagDatabase);
mLog = new TheLog(injector, translator, mTagDatabase);
mDumpsysDateFormat = injector.getDateFormat();
+ mContext = context;
}
/**
@@ -176,10 +184,24 @@ final class WakeLockLog {
try {
synchronized (mLock) {
pw.println("Wake Lock Log");
- LogEntry tempEntry = new LogEntry(); // Temporary entry for the iterator to reuse.
- final Iterator<LogEntry> iterator = mLog.getAllItems(tempEntry);
int numEvents = 0;
int numResets = 0;
+ SparseArray<String[]> uidToPackagesCache = new SparseArray();
+
+ for (int i = 0; i < mLog.mSavedAcquisitions.size(); i++) {
+ numEvents++;
+ LogEntry entry = mLog.mSavedAcquisitions.get(i);
+
+ entry.updatePackageName(uidToPackagesCache, mContext.getPackageManager());
+
+ if (DEBUG) {
+ pw.print("Saved acquisition no. " + i);
+ }
+ entry.dump(pw, mDumpsysDateFormat);
+ }
+
+ LogEntry tempEntry = new LogEntry(); // Temporary entry for the iterator to reuse.
+ final Iterator<LogEntry> iterator = mLog.getAllItems(tempEntry);
while (iterator.hasNext()) {
String address = null;
if (DEBUG) {
@@ -192,6 +214,8 @@ final class WakeLockLog {
numResets++;
} else {
numEvents++;
+ entry.updatePackageName(uidToPackagesCache,
+ mContext.getPackageManager());
if (DEBUG) {
pw.print(address);
}
@@ -381,6 +405,11 @@ final class WakeLockLog {
*/
public int flags;
+ /**
+ * The name of the package that acquired the wake lock
+ */
+ public String packageName;
+
LogEntry() {}
LogEntry(long time, int type, TagData tag, int flags) {
@@ -438,8 +467,13 @@ final class WakeLockLog {
}
sb.append(dateFormat.format(new Date(time)))
.append(" - ")
- .append(tag == null ? "---" : tag.ownerUid)
- .append(" - ")
+ .append(tag == null ? "---" : tag.ownerUid);
+ if (packageName != null) {
+ sb.append(" (");
+ sb.append(packageName);
+ sb.append(")");
+ }
+ sb.append(" - ")
.append(type == TYPE_ACQUIRE ? "ACQ" : "REL")
.append(" ")
.append(tag == null ? "UNKNOWN" : tag.tag);
@@ -463,6 +497,36 @@ final class WakeLockLog {
sb.append(",system-wakelock");
}
}
+
+ /**
+ * Update the package name using the cache if available or the package manager.
+ * @param uidToPackagesCache The cache of package names
+ * @param packageManager The package manager
+ */
+ public void updatePackageName(SparseArray<String[]> uidToPackagesCache,
+ PackageManager packageManager) {
+ if (tag == null) {
+ return;
+ }
+
+ String[] packages;
+ if (uidToPackagesCache.contains(tag.ownerUid)) {
+ packages = uidToPackagesCache.get(tag.ownerUid);
+ } else {
+ packages = packageManager.getPackagesForUid(tag.ownerUid);
+ uidToPackagesCache.put(tag.ownerUid, packages);
+ }
+
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ if (packages.length > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(packageName)
+ .append(",...");
+ packageName = sb.toString();
+ }
+ }
+ }
}
/**
@@ -744,6 +808,12 @@ final class WakeLockLog {
private final TagDatabase mTagDatabase;
+ /**
+ * Wake lock acquisition events should continue to be printed until their corresponding
+ * release event is removed from the log.
+ */
+ private final List<LogEntry> mSavedAcquisitions;
+
TheLog(Injector injector, EntryByteTranslator translator, TagDatabase tagDatabase) {
final int logSize = Math.max(injector.getLogSize(), LOG_SIZE_MIN);
mBuffer = new byte[logSize];
@@ -758,6 +828,8 @@ final class WakeLockLog {
removeTagIndex(index);
}
});
+
+ mSavedAcquisitions = new ArrayList();
}
/**
@@ -976,6 +1048,19 @@ final class WakeLockLog {
// Copy the contents of the start of the buffer to our temporary buffer.
LogEntry entry = readEntryAt(mStart, mStartTime, null);
+ if (entry.type == TYPE_ACQUIRE) {
+ // We'll continue to print the event until the corresponding release event is also
+ // removed from the log.
+ mSavedAcquisitions.add(entry);
+ } else if (entry.type == TYPE_RELEASE) {
+ // We no longer need to print the corresponding acquire event.
+ for (int i = 0; i < mSavedAcquisitions.size(); i++) {
+ if (Objects.equals(mSavedAcquisitions.get(i).tag, entry.tag)) {
+ mSavedAcquisitions.remove(i);
+ break;
+ }
+ }
+ }
if (DEBUG) {
Slog.d(TAG, "Removing oldest item at @ " + mStart + ", found: " + entry);
}
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 1fed7129d488..32104f68c091 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -113,6 +113,7 @@ import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
import com.android.internal.os.Clock;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
@@ -251,6 +252,9 @@ public class BatteryStatsImpl extends BatteryStats {
private static final LongCounter[] ZERO_LONG_COUNTER_ARRAY =
new LongCounter[]{ZERO_LONG_COUNTER};
+ @VisibleForTesting
+ protected CpuScalingPolicies mCpuScalingPolicies;
+
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@VisibleForTesting
@@ -324,6 +328,7 @@ public class BatteryStatsImpl extends BatteryStats {
* ModemActivityInfo must be available.
*/
public static final int PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX = 2;
+
@IntDef(flag = true, prefix = "PER_UID_MODEM_MODEL_", value = {
PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME,
PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX,
@@ -578,9 +583,7 @@ public class BatteryStatsImpl extends BatteryStats {
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
@VisibleForTesting
public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
- if (!initKernelSingleUidTimeReaderLocked()) {
- return;
- }
+ ensureKernelSingleUidTimeReaderLocked();
final Uid u = getUidStatsLocked(uid);
@@ -657,9 +660,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- if(!initKernelSingleUidTimeReaderLocked()) {
- return;
- }
+ ensureKernelSingleUidTimeReaderLocked();
// TODO(b/197162116): just get a list of UIDs
final SparseArray<long[]> allUidCpuFreqTimesMs =
@@ -719,24 +720,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- private boolean initKernelSingleUidTimeReaderLocked() {
- if (mKernelSingleUidTimeReader == null) {
- if (mPowerProfile == null) {
- return false;
- }
- if (mCpuFreqs == null) {
- mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
- }
- if (mCpuFreqs != null) {
- mKernelSingleUidTimeReader = new KernelSingleUidTimeReader(mCpuFreqs.length);
- } else {
- mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable();
- return false;
- }
+ private void ensureKernelSingleUidTimeReaderLocked() {
+ if (mKernelSingleUidTimeReader != null) {
+ return;
}
- mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable()
+
+ mKernelSingleUidTimeReader = new KernelSingleUidTimeReader(
+ mCpuScalingPolicies.getScalingStepCount());
+ mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.perClusterTimesAvailable()
&& mKernelSingleUidTimeReader.singleUidCpuTimesAvailable();
- return true;
}
public interface ExternalStatsSync {
@@ -1544,8 +1536,6 @@ public class BatteryStatsImpl extends BatteryStats {
private long mBatteryTimeToFullSeconds = -1;
- private boolean mCpuFreqsInitialized;
- private long[] mCpuFreqs;
private LongArrayMultiStateCounter.LongArrayContainer mTmpCpuTimeInFreq;
/**
@@ -8224,11 +8214,13 @@ public class BatteryStatsImpl extends BatteryStats {
mProcStateTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
- PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ mBsi.mCpuScalingPolicies.getScalingStepCount(),
timestampMs);
mProcStateScreenOffTimeMs =
new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
- PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ PROC_STATE_TIME_COUNTER_STATE_COUNT,
+ mBsi.mCpuScalingPolicies.getScalingStepCount(),
timestampMs);
}
@@ -9229,6 +9221,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ @Deprecated
public long getTimeAtCpuSpeed(int cluster, int step, int which) {
if (mCpuClusterSpeedTimesUs != null) {
if (cluster >= 0 && cluster < mCpuClusterSpeedTimesUs.length) {
@@ -10460,7 +10453,7 @@ public class BatteryStatsImpl extends BatteryStats {
cpuActiveCounter.setState(0, timestampMs);
if (mBsi.trackPerProcStateCpuTimes()) {
- final int cpuFreqCount = mBsi.getCpuFreqCount();
+ final int cpuFreqCount = mBsi.mCpuScalingPolicies.getScalingStepCount();
cpuTimeInFreqCounter = new LongArrayMultiStateCounter(1, cpuFreqCount);
@@ -10863,44 +10856,43 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- @Override
- public long[] getCpuFreqs() {
- if (!mCpuFreqsInitialized) {
- mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
- mCpuFreqsInitialized = true;
- }
- return mCpuFreqs;
- }
-
- @GuardedBy("this")
- @Override
- public int getCpuFreqCount() {
- final long[] cpuFreqs = getCpuFreqs();
- return cpuFreqs != null ? cpuFreqs.length : 0;
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ return mCpuScalingPolicies;
}
@GuardedBy("this")
private LongArrayMultiStateCounter.LongArrayContainer getCpuTimeInFreqContainer() {
if (mTmpCpuTimeInFreq == null) {
mTmpCpuTimeInFreq =
- new LongArrayMultiStateCounter.LongArrayContainer(getCpuFreqCount());
+ new LongArrayMultiStateCounter.LongArrayContainer(
+ mCpuScalingPolicies.getScalingStepCount());
}
return mTmpCpuTimeInFreq;
}
- public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
- EnergyStatsRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
- this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider);
+ public BatteryStatsImpl(@Nullable File systemDir, @NonNull Handler handler,
+ @Nullable PlatformIdleStateCallback cb, @Nullable EnergyStatsRetriever energyStatsCb,
+ @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
+ @NonNull CpuScalingPolicies cpuScalingPolicies) {
+ this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider,
+ powerProfile, cpuScalingPolicies);
}
- private BatteryStatsImpl(Clock clock, File systemDir, Handler handler,
- PlatformIdleStateCallback cb, EnergyStatsRetriever energyStatsCb,
- UserInfoProvider userInfoProvider) {
+ private BatteryStatsImpl(@NonNull Clock clock, @Nullable File systemDir,
+ @NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
+ @Nullable EnergyStatsRetriever energyStatsCb,
+ @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
+ @NonNull CpuScalingPolicies cpuScalingPolicies) {
init(clock);
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
+ mPowerProfile = powerProfile;
+ mCpuScalingPolicies = cpuScalingPolicies;
+
+ initPowerProfile();
+
if (systemDir == null) {
mStatsFile = null;
mCheckinFile = null;
@@ -11016,39 +11008,27 @@ public class BatteryStatsImpl extends BatteryStats {
mBatteryLevel = 0;
}
- /**
- * Injects a power profile.
- */
- @GuardedBy("this")
- public void setPowerProfileLocked(PowerProfile profile) {
- mPowerProfile = profile;
-
- int totalSpeedStepCount = 0;
-
- // We need to initialize the KernelCpuSpeedReaders to read from
- // the first cpu of each core. Once we have the PowerProfile, we have access to this
- // information.
- final int numClusters = mPowerProfile.getNumCpuClusters();
- mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
- int firstCpuOfCluster = 0;
- for (int i = 0; i < numClusters; i++) {
- final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
- mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
- numSpeedSteps);
- firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
- totalSpeedStepCount += numSpeedSteps;
+ private void initPowerProfile() {
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ mKernelCpuSpeedReaders = new KernelCpuSpeedReader[policies.length];
+ for (int i = 0; i < policies.length; i++) {
+ int[] cpus = mCpuScalingPolicies.getRelatedCpus(policies[i]);
+ int[] freqs = mCpuScalingPolicies.getFrequencies(policies[i]);
+ // We need to initialize the KernelCpuSpeedReaders to read from
+ // the first cpu of each core. Once we have the CpuScalingPolicy, we have access to this
+ // information.
+ mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(cpus[0], freqs.length);
}
// Initialize CPU power bracket map, which combines CPU states (cluster/freq pairs)
// into a small number of brackets
- mCpuPowerBracketMap = new int[totalSpeedStepCount];
+ mCpuPowerBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
int index = 0;
- int numCpuClusters = mPowerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- int steps = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int policy : policies) {
+ int steps = mCpuScalingPolicies.getFrequencies(policy).length;
for (int step = 0; step < steps; step++) {
mCpuPowerBracketMap[index++] =
- mPowerProfile.getPowerBracketForCpuCore(cluster, step);
+ mPowerProfile.getCpuPowerBracketForScalingStep(policy, step);
}
}
@@ -11057,7 +11037,7 @@ public class BatteryStatsImpl extends BatteryStats {
mCpuUsageDetails.cpuUsageMs = new long[cpuPowerBracketCount];
for (int i = 0; i < cpuPowerBracketCount; i++) {
mCpuUsageDetails.cpuBracketDescriptions[i] =
- mPowerProfile.getCpuPowerBracketDescription(i);
+ mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, i);
}
if (mEstimatedBatteryCapacityMah == -1) {
@@ -12179,7 +12159,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
- ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
+ ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo))
: mLastModemActivityInfo.getDelta(activityInfo);
mLastModemActivityInfo = activityInfo;
@@ -13573,18 +13554,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff,
long[] cpuClusterChargeUC) {
- if (mPowerProfile == null) {
- return;
- }
-
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "!Cpu updating!");
}
- if (mCpuFreqs == null) {
- mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
- }
-
// Calculate the wakelocks we have to distribute amongst. The system is excluded as it is
// usually holding the wakelock on behalf of an app.
// And Only distribute cpu power to wakelocks if the screen is off and we're on battery.
@@ -13616,31 +13589,37 @@ public class BatteryStatsImpl extends BatteryStats {
mCpuUidClusterTimeReader.readDelta(false, null);
mNumAllUidCpuTimeReads += 2;
}
- for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
- mKernelCpuSpeedReaders[cluster].readDelta();
+ for (int i = mKernelCpuSpeedReaders.length - 1; i >= 0; --i) {
+ if (mKernelCpuSpeedReaders[i] != null) {
+ mKernelCpuSpeedReaders[i].readDelta();
+ }
}
mSystemServerCpuThreadReader.readDelta();
return;
}
mUserInfoProvider.refreshUserIds();
- final SparseLongArray updatedUids = mCpuUidFreqTimeReader.perClusterTimesAvailable()
+ final SparseLongArray updatedUids = mCpuUidFreqTimeReader.allUidTimesAvailable()
? null : new SparseLongArray();
final CpuDeltaPowerAccumulator powerAccumulator;
if (mGlobalEnergyConsumerStats != null
&& mGlobalEnergyConsumerStats.isStandardBucketSupported(
- EnergyConsumerStats.POWER_BUCKET_CPU) && mCpuPowerCalculator != null) {
+ EnergyConsumerStats.POWER_BUCKET_CPU)) {
if (cpuClusterChargeUC == null) {
Slog.wtf(TAG,
"POWER_BUCKET_CPU supported but no EnergyConsumer Cpu Cluster charge "
+ "reported on updateCpuTimeLocked!");
powerAccumulator = null;
} else {
+ if (mCpuPowerCalculator == null) {
+ mCpuPowerCalculator = new CpuPowerCalculator(mCpuScalingPolicies,
+ mPowerProfile);
+ }
// Cpu EnergyConsumer is supported, create an object to accumulate the estimated
// charge consumption since the last cpu update
- final int numClusters = mPowerProfile.getNumCpuClusters();
- powerAccumulator = new CpuDeltaPowerAccumulator(mCpuPowerCalculator, numClusters);
+ powerAccumulator = new CpuDeltaPowerAccumulator(mCpuPowerCalculator,
+ mCpuScalingPolicies.getPolicies().length);
}
} else {
powerAccumulator = null;
@@ -13698,15 +13677,14 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_BINDER_STATS) {
Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
long binderThreadTimeMs = 0;
- int cpuIndex = 0;
final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
BatteryStats.STATS_SINCE_CHARGED);
int index = 0;
- int numCpuClusters = mPowerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ for (int policy : policies) {
StringBuilder sb = new StringBuilder();
- sb.append("cpu").append(cpuIndex).append(": [");
- int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ sb.append("policy").append(policy).append(": [");
+ int numSpeeds = mCpuScalingPolicies.getFrequencies(policy).length;
for (int speed = 0; speed < numSpeeds; speed++) {
if (speed != 0) {
sb.append(", ");
@@ -13717,7 +13695,6 @@ public class BatteryStatsImpl extends BatteryStats {
binderThreadTimeMs += binderCountMs;
index++;
}
- cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
Slog.d(TAG, sb.toString());
}
}
@@ -13768,10 +13745,12 @@ public class BatteryStatsImpl extends BatteryStats {
// Read the time spent for each cluster at various cpu frequencies.
final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
- clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
- if (clusterSpeedTimesMs[cluster] != null) {
- for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
- totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
+ if (mKernelCpuSpeedReaders[cluster] != null) {
+ clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
+ if (clusterSpeedTimesMs[cluster] != null) {
+ for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) {
+ totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed];
+ }
}
}
}
@@ -13786,13 +13765,13 @@ public class BatteryStatsImpl extends BatteryStats {
final Uid u = getUidStatsLocked(updatedUids.keyAt(i), elapsedRealtimeMs, uptimeMs);
final long appCpuTimeUs = updatedUids.valueAt(i);
// Add the cpu speeds to this UID.
- final int numClusters = mPowerProfile.getNumCpuClusters();
+ int[] policies = mCpuScalingPolicies.getPolicies();
if (u.mCpuClusterSpeedTimesUs == null ||
- u.mCpuClusterSpeedTimesUs.length != numClusters) {
- u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
+ u.mCpuClusterSpeedTimesUs.length != policies.length) {
+ u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[policies.length][];
}
- for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) {
+ for (int cluster = 0; cluster < policies.length; cluster++) {
final int speedsInCluster = clusterSpeedTimesMs[cluster].length;
if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster !=
u.mCpuClusterSpeedTimesUs[cluster].length) {
@@ -13948,7 +13927,8 @@ public class BatteryStatsImpl extends BatteryStats {
final boolean perClusterTimesAvailable =
mCpuUidFreqTimeReader.perClusterTimesAvailable();
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
- final int numClusters = mPowerProfile.getNumCpuClusters();
+ final int[] policies = mCpuScalingPolicies.getPolicies();
+ final int numClusters = policies.length;
mWakeLockAllocationsUs = null;
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
@@ -13989,19 +13969,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
int freqIndex = 0;
- for (int cluster = 0; cluster < numClusters; ++cluster) {
- final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int cluster = 0; cluster < numClusters; cluster++) {
+ final int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]);
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
- u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
+ u.mCpuClusterSpeedTimesUs[cluster].length != freqs.length) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
- u.mCpuClusterSpeedTimesUs[cluster]
- = new LongSamplingCounter[speedsInCluster];
+ u.mCpuClusterSpeedTimesUs[cluster] = new LongSamplingCounter[freqs.length];
}
if (numWakelocks > 0 && mWakeLockAllocationsUs[cluster] == null) {
- mWakeLockAllocationsUs[cluster] = new long[speedsInCluster];
+ mWakeLockAllocationsUs[cluster] = new long[freqs.length];
}
final LongSamplingCounter[] cpuTimesUs = u.mCpuClusterSpeedTimesUs[cluster];
- for (int speed = 0; speed < speedsInCluster; ++speed) {
+ for (int speed = 0; speed < freqs.length; ++speed) {
if (cpuTimesUs[speed] == null) {
cpuTimesUs[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
@@ -14041,7 +14020,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
for (int cluster = 0; cluster < numClusters; ++cluster) {
- final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ final int speedsInCluster =
+ mCpuScalingPolicies.getFrequencies(policies[cluster]).length;
if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
@@ -14171,6 +14151,9 @@ public class BatteryStatsImpl extends BatteryStats {
* Notifies BatteryStatsImpl that the system server is ready.
*/
public void onSystemReady() {
+ if (mCpuUidFreqTimeReader != null) {
+ mCpuUidFreqTimeReader.onSystemReady();
+ }
mSystemReady = true;
}
@@ -14632,17 +14615,13 @@ public class BatteryStatsImpl extends BatteryStats {
// Inform StatsLog of setBatteryState changes.
private void reportChangesToStatsLog(final int status, final int plugType, final int level) {
- if (!mHaveBatteryLevel) {
- return;
- }
-
- if (mBatteryStatus != status) {
+ if (!mHaveBatteryLevel || mBatteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
- if (mBatteryPlugType != plugType) {
+ if (!mHaveBatteryLevel || mBatteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
- if (mBatteryLevel != level) {
+ if (!mHaveBatteryLevel || mBatteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
@@ -15245,9 +15224,6 @@ public class BatteryStatsImpl extends BatteryStats {
if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_BLUETOOTH]) {
mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
}
- if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_CPU]) {
- mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
- }
if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO]) {
mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile);
}
@@ -15635,7 +15611,7 @@ public class BatteryStatsImpl extends BatteryStats {
pw.print(" ");
pw.print(bracket);
pw.print(": ");
- pw.println(mPowerProfile.getCpuPowerBracketDescription(bracket));
+ pw.println(mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, bracket));
}
}
@@ -16126,7 +16102,9 @@ public class BatteryStatsImpl extends BatteryStats {
if (in.readInt() != 0) {
final int numClusters = in.readInt();
- if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
+ int[] policies =
+ mCpuScalingPolicies != null ? mCpuScalingPolicies.getPolicies() : null;
+ if (policies != null && policies.length != numClusters) {
throw new ParcelFormatException("Incompatible cpu cluster arrangement");
}
detachIfNotNull(u.mCpuClusterSpeedTimesUs);
@@ -16134,8 +16112,9 @@ public class BatteryStatsImpl extends BatteryStats {
for (int cluster = 0; cluster < numClusters; cluster++) {
if (in.readInt() != 0) {
final int NSB = in.readInt();
- if (mPowerProfile != null &&
- mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
+ if (policies != null
+ && mCpuScalingPolicies.getFrequencies(policies[cluster]).length
+ != NSB) {
throw new ParcelFormatException("File corrupt: too many speed bins " +
NSB);
}
@@ -16180,7 +16159,7 @@ public class BatteryStatsImpl extends BatteryStats {
detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
- getCpuFreqCount(), mClock.elapsedRealtime());
+ mCpuScalingPolicies.getScalingStepCount(), mClock.elapsedRealtime());
}
detachIfNotNull(u.mProcStateScreenOffTimeMs);
@@ -16191,7 +16170,7 @@ public class BatteryStatsImpl extends BatteryStats {
detachIfNotNull(u.mProcStateScreenOffTimeMs);
u.mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in,
mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT,
- getCpuFreqCount(), mClock.elapsedRealtime());
+ mCpuScalingPolicies.getScalingStepCount(), mClock.elapsedRealtime());
}
if (in.readInt() != 0) {
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 ebd4aec3aef9..3a3273328a7a 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -31,6 +31,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import java.util.ArrayList;
@@ -47,6 +48,7 @@ public class BatteryUsageStatsProvider {
private final BatteryStats mStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final PowerProfile mPowerProfile;
+ private final CpuScalingPolicies mCpuScalingPolicies;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
@@ -63,6 +65,7 @@ public class BatteryUsageStatsProvider {
mPowerProfile = stats instanceof BatteryStatsImpl
? ((BatteryStatsImpl) stats).getPowerProfile()
: new PowerProfile(context);
+ mCpuScalingPolicies = stats.getCpuScalingPolicies();
}
private List<PowerCalculator> getPowerCalculators() {
@@ -72,7 +75,7 @@ public class BatteryUsageStatsProvider {
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
+ mPowerCalculators.add(new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
@@ -97,7 +100,8 @@ public class BatteryUsageStatsProvider {
// It is important that SystemServicePowerCalculator be applied last,
// because it re-attributes some of the power estimated by the other
// calculators.
- mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
+ mPowerCalculators.add(
+ new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile));
}
}
return mPowerCalculators;
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java b/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
index 5074838c037f..ce8680f6cefd 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
@@ -24,6 +24,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import java.util.Arrays;
@@ -32,6 +33,8 @@ public class CpuPowerCalculator extends PowerCalculator {
private static final String TAG = "CpuPowerCalculator";
private static final boolean DEBUG = PowerCalculator.DEBUG;
private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
+ private final CpuScalingPolicies mCpuScalingPolicies;
private final int mNumCpuClusters;
// Time-in-state based CPU power estimation model computes the estimated power
@@ -59,34 +62,33 @@ public class CpuPowerCalculator extends PowerCalculator {
public long[] cpuFreqTimes;
}
- public CpuPowerCalculator(PowerProfile profile) {
- mNumCpuClusters = profile.getNumCpuClusters();
+ public CpuPowerCalculator(CpuScalingPolicies cpuScalingPolicies, PowerProfile profile) {
+ mCpuScalingPolicies = cpuScalingPolicies;
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ mNumCpuClusters = policies.length;
mCpuActivePowerEstimator = new UsageBasedPowerEstimator(
profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
- mPerClusterPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters];
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- mPerClusterPowerEstimators[cluster] = new UsageBasedPowerEstimator(
- profile.getAveragePowerForCpuCluster(cluster));
- }
-
- int freqCount = 0;
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- freqCount += profile.getNumSpeedStepsInCpuCluster(cluster);
+ mPerClusterPowerEstimators = new UsageBasedPowerEstimator[policies.length];
+ for (int i = 0; i < policies.length; i++) {
+ mPerClusterPowerEstimators[i] = new UsageBasedPowerEstimator(
+ profile.getAveragePowerForCpuScalingPolicy(policies[i]));
}
+ mPerCpuFreqPowerEstimators =
+ new UsageBasedPowerEstimator[cpuScalingPolicies.getScalingStepCount()];
mPerCpuFreqPowerEstimatorsByCluster = new UsageBasedPowerEstimator[mNumCpuClusters][];
- mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[freqCount];
int index = 0;
- for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
- final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int cluster = 0; cluster < policies.length; cluster++) {
+ int policy = policies[cluster];
+ int[] freqs = cpuScalingPolicies.getFrequencies(policy);
mPerCpuFreqPowerEstimatorsByCluster[cluster] =
- new UsageBasedPowerEstimator[speedsForCluster];
- for (int speed = 0; speed < speedsForCluster; speed++) {
+ new UsageBasedPowerEstimator[freqs.length];
+ for (int step = 0; step < freqs.length; step++) {
final UsageBasedPowerEstimator estimator = new UsageBasedPowerEstimator(
- profile.getAveragePowerForCpuCore(cluster, speed));
- mPerCpuFreqPowerEstimatorsByCluster[cluster][speed] = estimator;
+ profile.getAveragePowerForCpuScalingStep(policy, step));
+ mPerCpuFreqPowerEstimatorsByCluster[cluster][step] = estimator;
mPerCpuFreqPowerEstimators[index++] = estimator;
}
}
@@ -105,7 +107,7 @@ public class CpuPowerCalculator extends PowerCalculator {
BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
Result result = new Result();
if (query.isProcessStateDataNeeded()) {
- result.cpuFreqTimes = new long[batteryStats.getCpuFreqCount()];
+ result.cpuFreqTimes = new long[mCpuScalingPolicies.getScalingStepCount()];
}
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
@@ -333,7 +335,7 @@ public class CpuPowerCalculator extends PowerCalculator {
* @param durationsMs duration of CPU usage.
* @return a double in milliamp-hours of estimated active CPU power consumption.
*/
- public double calculateActiveCpuPowerMah(long durationsMs) {
+ private double calculateActiveCpuPowerMah(long durationsMs) {
return mCpuActivePowerEstimator.calculatePower(durationsMs);
}
diff --git a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java b/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
index aa17d4fb69bf..1d5d93eb411a 100644
--- a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
@@ -25,6 +25,7 @@ import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
/**
@@ -39,23 +40,19 @@ public class SystemServicePowerCalculator extends PowerCalculator {
// to this layout:
// {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
private final UsageBasedPowerEstimator[] mPowerEstimators;
- private final com.android.server.power.stats.CpuPowerCalculator mCpuPowerCalculator;
-
- public SystemServicePowerCalculator(PowerProfile powerProfile) {
- mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
- int numFreqs = 0;
- final int numCpuClusters = powerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster);
- }
+ private final CpuPowerCalculator mCpuPowerCalculator;
- mPowerEstimators = new UsageBasedPowerEstimator[numFreqs];
+ public SystemServicePowerCalculator(CpuScalingPolicies cpuScalingPolicies,
+ PowerProfile powerProfile) {
+ mCpuPowerCalculator = new CpuPowerCalculator(cpuScalingPolicies, powerProfile);
+ mPowerEstimators = new UsageBasedPowerEstimator[cpuScalingPolicies.getScalingStepCount()];
int index = 0;
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ int[] policies = cpuScalingPolicies.getPolicies();
+ for (int policy : policies) {
+ final int numSpeeds = cpuScalingPolicies.getFrequencies(policy).length;
for (int speed = 0; speed < numSpeeds; speed++) {
mPowerEstimators[index++] = new UsageBasedPowerEstimator(
- powerProfile.getAveragePowerForCpuCore(cluster, speed));
+ powerProfile.getAveragePowerForCpuScalingStep(policy, speed));
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 712a6964e82d..f047f564538d 100644
--- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -32,12 +32,12 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
-import android.util.TimeSparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -74,12 +74,12 @@ public class CpuWakeupStats {
private final WakingActivityHistory mRecentWakingActivity;
@VisibleForTesting
- final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+ final LongSparseArray<Wakeup> mWakeupEvents = new LongSparseArray<>();
/* Maps timestamp -> {subsystem -> {uid -> procState}} */
@VisibleForTesting
- final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
- new TimeSparseArray<>();
+ final LongSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
+ new LongSparseArray<>();
final SparseIntArray mUidProcStates = new SparseIntArray();
private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4);
@@ -218,11 +218,11 @@ public class CpuWakeupStats {
// the last wakeup and its attribution (if computed) is always stored, even if that wakeup
// had occurred before retentionDuration.
final long retentionDuration = mConfig.WAKEUP_STATS_RETENTION_MS;
- int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
+ int lastIdx = mWakeupEvents.lastIndexOnOrBefore(elapsedRealtime - retentionDuration);
for (int i = lastIdx; i >= 0; i--) {
mWakeupEvents.removeAt(i);
}
- lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
+ lastIdx = mWakeupAttribution.lastIndexOnOrBefore(elapsedRealtime - retentionDuration);
for (int i = lastIdx; i >= 0; i--) {
mWakeupAttribution.removeAt(i);
}
@@ -273,9 +273,9 @@ public class CpuWakeupStats {
SparseIntArray uidProcStates) {
final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;
- final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
+ final int startIdx = mWakeupEvents.firstIndexOnOrAfter(
activityElapsed - matchingWindowMillis);
- final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
+ final int endIdx = mWakeupEvents.lastIndexOnOrBefore(
activityElapsed + matchingWindowMillis);
for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
@@ -404,7 +404,7 @@ public class CpuWakeupStats {
static final class WakingActivityHistory {
private LongSupplier mRetentionSupplier;
@VisibleForTesting
- final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();
+ final SparseArray<LongSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();
WakingActivityHistory(LongSupplier retentionSupplier) {
mRetentionSupplier = retentionSupplier;
@@ -414,9 +414,9 @@ public class CpuWakeupStats {
if (uidProcStates == null) {
return;
}
- TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
+ LongSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
if (wakingActivity == null) {
- wakingActivity = new TimeSparseArray<>();
+ wakingActivity = new LongSparseArray<>();
mWakingActivity.put(subsystem, wakingActivity);
}
final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime);
@@ -435,7 +435,7 @@ public class CpuWakeupStats {
// Limit activity history per subsystem to the last retention period as supplied by
// mRetentionSupplier. Note that the last activity is always present, even if it
// occurred before the retention period.
- final int endIdx = wakingActivity.closestIndexOnOrBefore(
+ final int endIdx = wakingActivity.lastIndexOnOrBefore(
elapsedRealtime - mRetentionSupplier.getAsLong());
for (int i = endIdx; i >= 0; i--) {
wakingActivity.removeAt(i);
@@ -445,11 +445,11 @@ public class CpuWakeupStats {
SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
final SparseIntArray uidsToReturn = new SparseIntArray();
- final TimeSparseArray<SparseIntArray> activityForSubsystem =
+ final LongSparseArray<SparseIntArray> activityForSubsystem =
mWakingActivity.get(subsystem);
if (activityForSubsystem != null) {
- final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
- final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
+ final int startIdx = activityForSubsystem.firstIndexOnOrAfter(startElapsed);
+ final int endIdx = activityForSubsystem.lastIndexOnOrBefore(endElapsed);
for (int i = endIdx; i >= startIdx; i--) {
final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i);
for (int j = 0; j < uidsForTime.size(); j++) {
@@ -463,8 +463,8 @@ public class CpuWakeupStats {
activityForSubsystem.removeAt(i);
}
// Generally waking activity is a high frequency occurrence for any subsystem, so we
- // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
- // This will leave one TimeSparseArray per subsystem, which are few right now.
+ // don't delete the LongSparseArray even if it is now empty, to avoid object churn.
+ // This will leave one LongSparseArray per subsystem, which are few right now.
}
return uidsToReturn.size() > 0 ? uidsToReturn : null;
}
@@ -474,7 +474,7 @@ public class CpuWakeupStats {
pw.increaseIndent();
for (int i = 0; i < mWakingActivity.size(); i++) {
pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
- final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
+ final LongSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
if (wakingActivity == null) {
continue;
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index ecd5bd22cd03..76126df14bfd 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -1487,6 +1487,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void binderDied() {
mSensorPrivacyServiceImpl.removeSensorPrivacyListener(mListener);
+ mSensorPrivacyServiceImpl.removeToggleSensorPrivacyListener(mListener);
}
public void destroy() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 6e9a22c7872b..efd8b6d9a943 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -19,7 +19,6 @@ package com.android.server.statusbar;
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.IBinder;
import android.view.WindowInsets.Type.InsetsType;
@@ -55,13 +54,13 @@ public interface StatusBarManagerInternal {
* @param displayId The display to which the IME is bound to.
* @param token The IME token.
* @param vis Bit flags about the IME visibility.
+ * (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
* @param backDisposition Bit flags about the IME back disposition.
+ * (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
* @param showImeSwitcher {@code true} when the IME switcher button should be shown.
*/
- void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher);
+ void setImeWindowStatus(int displayId, IBinder token, int vis,
+ int backDisposition, boolean showImeSwitcher);
/**
* See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2c381ca3bd41..cc849b6fbf91 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -59,7 +59,6 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.net.Uri;
@@ -533,9 +532,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition,
showImeSwitcher);
@@ -1267,14 +1264,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void setImeWindowStatus(int displayId, final IBinder token,
- @InputMethodService.ImeWindowVisibility final int vis,
- @InputMethodService.BackDispositionMode final int backDisposition,
- final boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
+ final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
if (SPEW) {
- Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
+ Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
}
synchronized(mLock) {
@@ -1337,9 +1332,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
- @InputMethodService.ImeWindowVisibility
private int mImeWindowVis = 0;
- @InputMethodService.BackDispositionMode
private int mImeBackDisposition = 0;
private boolean mShowImeSwitcher = false;
private IBinder mImeToken = null;
@@ -1384,8 +1377,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return mDisabled1 == disabled1 && mDisabled2 == disabled2;
}
- private void setImeWindowState(@InputMethodService.ImeWindowVisibility final int vis,
- @InputMethodService.BackDispositionMode final int backDisposition,
+ private void setImeWindowState(final int vis, final int backDisposition,
final boolean showImeSwitcher, final IBinder token) {
mImeWindowVis = vis;
mImeBackDisposition = backDisposition;
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index d63a908517fb..0ab6d5769b5e 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -396,6 +396,16 @@ class TvInputHardwareManager implements TvInputHal.Callback {
}
}
+ public void updateInputInfo(TvInputInfo info) {
+ synchronized (mLock) {
+ if (!mInputMap.containsKey(info.getId())) {
+ return;
+ }
+ Slog.w(TAG, "update inputInfo for input id " + info.getId());
+ mInputMap.put(info.getId(), info);
+ }
+ }
+
/**
* Create a TvInputHardware object with a specific deviceId. One service at a time can access
* the object, and if more than one process attempts to create hardware with the same deviceId,
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a05bd2d008da..19ee554fd973 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1067,6 +1067,11 @@ public final class TvInputManagerService extends SystemService {
}
inputState.info = inputInfo;
inputState.uid = getInputUid(inputInfo);
+ ServiceState serviceState = userState.serviceStateMap.get(inputInfo.getComponent());
+ if (serviceState != null && serviceState.isHardware) {
+ serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
+ mTvInputHardwareManager.updateInputInfo(inputInfo);
+ }
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
diff --git a/services/core/java/com/android/server/utils/AlarmQueue.java b/services/core/java/com/android/server/utils/AlarmQueue.java
index 09ba19508476..83605ae09e97 100644
--- a/services/core/java/com/android/server/utils/AlarmQueue.java
+++ b/services/core/java/com/android/server/utils/AlarmQueue.java
@@ -151,6 +151,10 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener {
@GuardedBy("mLock")
@ElapsedRealtimeLong
private long mTriggerTimeElapsed = NOT_SCHEDULED;
+ /** The last time an alarm went off (ie. the last time {@link #onAlarm()} was called}). */
+ @GuardedBy("mLock")
+ @ElapsedRealtimeLong
+ private long mLastFireTimeElapsed;
/**
* @param alarmTag The tag to use when scheduling the alarm with AlarmManager.
@@ -284,7 +288,7 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener {
/** Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue after now. */
@GuardedBy("mLock")
private void setNextAlarmLocked() {
- setNextAlarmLocked(mInjector.getElapsedRealtime());
+ setNextAlarmLocked(mLastFireTimeElapsed + mMinTimeBetweenAlarmsMs);
}
/**
@@ -334,6 +338,7 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener {
final ArraySet<K> expired = new ArraySet<>();
synchronized (mLock) {
final long nowElapsed = mInjector.getElapsedRealtime();
+ mLastFireTimeElapsed = nowElapsed;
while (mAlarmPriorityQueue.size() > 0) {
final Pair<K, Long> alarm = mAlarmPriorityQueue.peek();
if (alarm.second <= nowElapsed) {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b296ef2a1443..1ff01a6c70bf 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -1049,7 +1049,11 @@ public class VrManagerService extends SystemService
for (ComponentName c : possibleServices) {
if (Objects.equals(c.getPackageName(), pkg)) {
- nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ try {
+ nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not grant NLS access to package " + pkg, e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 11d1126c9ae4..25a0a527cd0c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -955,7 +955,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
wpdData.mWidth, wpdData.mHeight,
- wpdData.mPadding, mDisplayId, wallpaper.mWhich);
+ wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
if (wallpaper != null && !wallpaper.wallpaperUpdating
@@ -3158,7 +3158,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (which == FLAG_SYSTEM && systemIsStatic && systemIsBoth) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ " updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)
+ && !isLockscreenLiveWallpaperEnabled()) {
+ which |= FLAG_LOCK;
+ }
}
wallpaper = getWallpaperSafeLocked(userId, which);
@@ -3186,13 +3189,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private void migrateStaticSystemToLockWallpaperLocked(int userId) {
+ private boolean migrateStaticSystemToLockWallpaperLocked(int userId) {
WallpaperData sysWP = mWallpaperMap.get(userId);
if (sysWP == null) {
if (DEBUG) {
Slog.i(TAG, "No system wallpaper? Not tracking for lock-only");
}
- return;
+ return true;
}
// We know a-priori that there is no lock-only wallpaper currently
@@ -3219,9 +3222,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
SELinux.restorecon(lockWP.getWallpaperFile());
mLastLockWallpaper = lockWP;
}
+ return true;
} catch (ErrnoException e) {
- Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
+ // can happen when migrating default wallpaper (which is not stored in wallpaperFile)
+ Slog.w(TAG, "Couldn't migrate system wallpaper: " + e.getMessage());
clearWallpaperBitmaps(lockWP);
+ return false;
}
}
@@ -3433,7 +3439,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// therefore it's a shared system+lock image that we need to migrate.
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)) {
+ which |= FLAG_LOCK;
+ }
}
}
@@ -3588,7 +3596,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final int hasPrivilege = mIPackageManager.checkPermission(
android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
serviceUserId);
- if (hasPrivilege != PERMISSION_GRANTED) {
+ // All watch wallpapers support ambient mode by default.
+ if (hasPrivilege != PERMISSION_GRANTED
+ && !mIPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
String msg = "Selected service does not have "
+ android.Manifest.permission.AMBIENT_WALLPAPER
+ ": " + componentName;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f3001133338a..b3ae2ee30f22 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1280,45 +1280,53 @@ final class AccessibilityController {
}
void drawIfNeeded(SurfaceControl.Transaction t) {
+ // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
+ // using WindowManagerGlobalLock. Grab copies of these values before
+ // drawing on the canvas so that drawing can be performed outside of the lock.
+ int alpha;
+ Rect drawingRect = null;
+ Region drawingBounds = null;
synchronized (mService.mGlobalLock) {
if (!mInvalidated) {
return;
}
mInvalidated = false;
- if (mAlpha > 0) {
- Canvas canvas = null;
- try {
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
- canvas = mSurface.lockCanvas(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
- }
- } catch (IllegalArgumentException iae) {
- /* ignore */
- } catch (Surface.OutOfResourcesException oore) {
- /* ignore */
- }
- if (canvas == null) {
- return;
+
+ alpha = mAlpha;
+ if (alpha > 0) {
+ drawingBounds = new Region(mBounds);
+ // Empty dirty rectangle means unspecified.
+ if (mDirtyRect.isEmpty()) {
+ mBounds.getBounds(mDirtyRect);
}
+ mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
+ drawingRect = new Rect(mDirtyRect);
if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Bounds: " + mBounds);
+ Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds);
+ Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect);
}
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(mAlpha);
- Path path = mBounds.getBoundaryPath();
- canvas.drawPath(path, mPaint);
-
- mSurface.unlockCanvasAndPost(canvas);
- t.show(mSurfaceControl);
- } else {
- t.hide(mSurfaceControl);
}
}
+
+ // Draw without holding WindowManagerGlobalLock.
+ if (alpha > 0) {
+ Canvas canvas = null;
+ try {
+ canvas = mSurface.lockCanvas(drawingRect);
+ } catch (IllegalArgumentException | OutOfResourcesException e) {
+ /* ignore */
+ }
+ if (canvas == null) {
+ return;
+ }
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mPaint.setAlpha(alpha);
+ canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
+ mSurface.unlockCanvasAndPost(canvas);
+ t.show(mSurfaceControl);
+ } else {
+ t.hide(mSurfaceControl);
+ }
}
void releaseSurface() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1636e466a503..5165e86bcf02 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4574,6 +4574,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
task.forAllActivities(fromActivity -> {
if (fromActivity == this) return true;
+ // The snapshot starting window could remove itself when receive resized request without
+ // redraw, so transfer it to a different size activity could only cause flicker.
+ // By schedule remove snapshot starting window, the remove process will happen when
+ // transition ready, transition ready means the app window is drawn.
+ final StartingData tmpStartingData = fromActivity.mStartingData;
+ if (tmpStartingData != null && tmpStartingData.mAssociatedTask == null
+ && mTransitionController.isCollecting(fromActivity)
+ && tmpStartingData instanceof SnapshotStartingData) {
+ final Rect fromBounds = fromActivity.getBounds();
+ final Rect myBounds = getBounds();
+ if (!fromBounds.equals(myBounds)) {
+ // Mark as no animation, so these changes won't merge into playing transition.
+ if (mTransitionController.inPlayingTransition(fromActivity)) {
+ mTransitionController.setNoAnimation(this);
+ mTransitionController.setNoAnimation(fromActivity);
+ }
+ fromActivity.removeStartingWindow();
+ return true;
+ }
+ }
return !fromActivity.isVisibleRequested() && transferStartingWindow(fromActivity);
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 32f1f42aacb9..a2547fd437d1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -23,6 +23,7 @@ import android.app.ActivityManager;
import android.app.AppProtoEnums;
import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
+import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
@@ -308,6 +309,12 @@ public abstract class ActivityTaskManagerInternal {
public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent);
/**
+ * Starts a dream activity in the DreamService's process.
+ */
+ public abstract IAppTask startDreamActivity(@NonNull Intent intent, int callingUid,
+ int callingPid);
+
+ /**
* Set a uid that is allowed to bypass stopped app switches, launching an app
* whenever it wants.
*
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 41b3aad2415d..aeaf78327d57 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -546,6 +546,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
private volatile long mLastStopAppSwitchesTime;
+ @GuardedBy("itself")
private final List<AnrController> mAnrController = new ArrayList<>();
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -733,7 +734,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private boolean mShowDialogs = true;
/** Set if we are shutting down the system, similar to sleeping. */
- boolean mShuttingDown = false;
+ volatile boolean mShuttingDown;
/**
* We want to hold a wake lock while running a voice interaction session, since
@@ -1466,23 +1467,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return false;
}
- private void enforceCallerIsDream(String callerPackageName) {
- final long origId = Binder.clearCallingIdentity();
- try {
- if (!canLaunchDreamActivity(callerPackageName)) {
- throw new SecurityException("The dream activity can be started only when the device"
- + " is dreaming and only by the active dream package.");
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public boolean startDreamActivity(@NonNull Intent intent) {
- assertPackageMatchesCallingUid(intent.getPackage());
- enforceCallerIsDream(intent.getPackage());
-
+ private IAppTask startDreamActivityInternal(@NonNull Intent intent, int callingUid,
+ int callingPid) {
final ActivityInfo a = new ActivityInfo();
a.theme = com.android.internal.R.style.Theme_Dream;
a.exported = true;
@@ -1500,7 +1486,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
synchronized (mGlobalLock) {
- final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+ final WindowProcessController process = mProcessMap.getProcess(callingPid);
a.packageName = process.mInfo.packageName;
a.applicationInfo = process.mInfo;
@@ -1508,26 +1494,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
a.uiOptions = process.mInfo.uiOptions;
a.taskAffinity = "android:" + a.packageName + "/dream";
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final long origId = Binder.clearCallingIdentity();
- try {
- getActivityStartController().obtainStarter(intent, "dream")
- .setCallingUid(callingUid)
- .setCallingPid(callingPid)
- .setCallingPackage(intent.getPackage())
- .setActivityInfo(a)
- .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
- // To start the dream from background, we need to start it from a persistent
- // system process. Here we set the real calling uid to the system server uid
- .setRealCallingUid(Binder.getCallingUid())
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
- .execute();
- return true;
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+ getActivityStartController().obtainStarter(intent, "dream")
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
+ .setCallingPackage(intent.getPackage())
+ .setActivityInfo(a)
+ .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
+ .setOutActivity(outActivity)
+ // To start the dream from background, we need to start it from a persistent
+ // system process. Here we set the real calling uid to the system server uid
+ .setRealCallingUid(Binder.getCallingUid())
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .execute();
+
+ final ActivityRecord started = outActivity[0];
+ final IAppTask appTask = started == null ? null :
+ new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
+ return appTask;
}
}
@@ -2298,14 +2283,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** Register an {@link AnrController} to control the ANR dialog behavior */
public void registerAnrController(AnrController controller) {
- synchronized (mGlobalLock) {
+ synchronized (mAnrController) {
mAnrController.add(controller);
}
}
/** Unregister an {@link AnrController} */
public void unregisterAnrController(AnrController controller) {
- synchronized (mGlobalLock) {
+ synchronized (mAnrController) {
mAnrController.remove(controller);
}
}
@@ -2321,7 +2306,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ArrayList<AnrController> controllers;
- synchronized (mGlobalLock) {
+ synchronized (mAnrController) {
controllers = new ArrayList<>(mAnrController);
}
@@ -5925,6 +5910,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public IAppTask startDreamActivity(@NonNull Intent intent, int callingUid, int callingPid) {
+ return startDreamActivityInternal(intent, callingUid, callingPid);
+ }
+
+ @Override
public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
return;
@@ -6034,15 +6024,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean isShuttingDown() {
- synchronized (mGlobalLock) {
- return mShuttingDown;
- }
+ return mShuttingDown;
}
@Override
public boolean shuttingDown(boolean booted, int timeout) {
+ mShuttingDown = true;
synchronized (mGlobalLock) {
- mShuttingDown = true;
mRootWindowContainer.prepareForShutdown();
updateEventDispatchingLocked(booted);
notifyTaskPersisterLocked(null, true);
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index f7ccc0d91969..0273a30e157c 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
+import android.annotation.NonNull;
import android.annotation.UiThread;
+import android.annotation.WorkerThread;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -30,14 +32,18 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.IoThread;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,9 +51,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Manages warning dialogs shown during application lifecycle.
@@ -61,11 +65,12 @@ class AppWarnings {
public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
public static final int FLAG_HIDE_DEPRECATED_ABI = 0x08;
- private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
+ @GuardedBy("mPackageFlags")
+ private final ArrayMap<String, Integer> mPackageFlags = new ArrayMap<>();
private final ActivityTaskManagerService mAtm;
private final Context mUiContext;
- private final ConfigHandler mHandler;
+ private final WriteConfigTask mWriteConfigTask;
private final UiHandler mUiHandler;
private final AtomicFile mConfigFile;
@@ -75,30 +80,20 @@ class AppWarnings {
private DeprecatedAbiDialog mDeprecatedAbiDialog;
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
- private HashSet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
- new HashSet<>();
+ private final ArraySet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
+ new ArraySet<>();
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
mAlwaysShowUnsupportedCompileSdkWarningActivities.add(activity);
}
- /**
- * Creates a new warning dialog manager.
- * <p>
- * <strong>Note:</strong> Must be called from the ActivityManagerService thread.
- *
- * @param atm
- * @param uiContext
- * @param handler
- * @param uiHandler
- * @param systemDir
- */
+ /** Creates a new warning dialog manager. */
public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
Handler uiHandler, File systemDir) {
mAtm = atm;
mUiContext = uiContext;
- mHandler = new ConfigHandler(handler.getLooper());
+ mWriteConfigTask = new WriteConfigTask();
mUiHandler = new UiHandler(uiHandler.getLooper());
mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
@@ -256,8 +251,9 @@ class AppWarnings {
mUiHandler.hideDialogsForPackage(name);
synchronized (mPackageFlags) {
- mPackageFlags.remove(name);
- mHandler.scheduleWrite();
+ if (mPackageFlags.remove(name) != null) {
+ mWriteConfigTask.schedule();
+ }
}
}
@@ -425,7 +421,7 @@ class AppWarnings {
} else {
mPackageFlags.remove(name);
}
- mHandler.scheduleWrite();
+ mWriteConfigTask.schedule();
}
}
}
@@ -556,46 +552,30 @@ class AppWarnings {
}
}
- /**
- * Handles messages on the ActivityTaskManagerService thread.
- */
- private final class ConfigHandler extends Handler {
- private static final int MSG_WRITE = 1;
-
- private static final int DELAY_MSG_WRITE = 10000;
-
- public ConfigHandler(Looper looper) {
- super(looper, null, true);
- }
+ private final class WriteConfigTask implements Runnable {
+ private static final long WRITE_CONFIG_DELAY_MS = 10000;
+ final AtomicReference<ArrayMap<String, Integer>> mPendingPackageFlags =
+ new AtomicReference<>();
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_WRITE:
- writeConfigToFileAmsThread();
- break;
+ public void run() {
+ final ArrayMap<String, Integer> packageFlags = mPendingPackageFlags.getAndSet(null);
+ if (packageFlags != null) {
+ writeConfigToFile(packageFlags);
}
}
- public void scheduleWrite() {
- removeMessages(MSG_WRITE);
- sendEmptyMessageDelayed(MSG_WRITE, DELAY_MSG_WRITE);
+ @GuardedBy("mPackageFlags")
+ void schedule() {
+ if (mPendingPackageFlags.getAndSet(new ArrayMap<>(mPackageFlags)) == null) {
+ IoThread.getHandler().postDelayed(this, WRITE_CONFIG_DELAY_MS);
+ }
}
}
- /**
- * Writes the configuration file.
- * <p>
- * <strong>Note:</strong> Should be called from the ActivityManagerService thread unless you
- * don't care where you're doing I/O operations. But you <i>do</i> care, don't you?
- */
- private void writeConfigToFileAmsThread() {
- // Create a shallow copy so that we don't have to synchronize on config.
- final HashMap<String, Integer> packageFlags;
- synchronized (mPackageFlags) {
- packageFlags = new HashMap<>(mPackageFlags);
- }
-
+ /** Writes the configuration file. */
+ @WorkerThread
+ private void writeConfigToFile(@NonNull ArrayMap<String, Integer> packageFlags) {
FileOutputStream fos = null;
try {
fos = mConfigFile.startWrite();
@@ -605,9 +585,9 @@ class AppWarnings {
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "packages");
- for (Map.Entry<String, Integer> entry : packageFlags.entrySet()) {
- String pkg = entry.getKey();
- int mode = entry.getValue();
+ for (int i = 0; i < packageFlags.size(); i++) {
+ final String pkg = packageFlags.keyAt(i);
+ final int mode = packageFlags.valueAt(i);
if (mode == 0) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index b216578262b4..188f4d0b8ced 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -280,7 +280,7 @@ public class BackgroundActivityStartController {
// visible window.
if (Process.isSdkSandboxUid(realCallingUid)) {
int realCallingSdkSandboxUidToAppUid =
- Process.getAppUidForSdkSandboxUid(UserHandle.getAppId(realCallingUid));
+ Process.getAppUidForSdkSandboxUid(realCallingUid);
if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
return logStartAllowedAndReturnCode(BAL_ALLOW_SDK_SANDBOX,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 12b5f5f774de..bfd2a10a8882 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3701,6 +3701,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mInputMonitor.dump(pw, " ");
pw.println();
mInsetsStateController.dump(prefix, pw);
+ mInsetsPolicy.dump(prefix, pw);
mDwpcHelper.dump(prefix, pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a61be9ea556d..ab89b8748e2f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -327,8 +327,6 @@ public class DisplayPolicy {
private WindowState mTopFullscreenOpaqueWindowState;
private boolean mTopIsFullscreen;
private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
- private boolean mForceConsumeSystemBars;
- private boolean mForceShowSystemBars;
/**
* Windows that provides gesture insets. If multiple windows provide gesture insets at the same
@@ -1286,18 +1284,10 @@ public class DisplayPolicy {
return ANIMATION_STYLEABLE;
}
- /**
- * @return true if the system bars are forced to be consumed
- */
+ // TODO (b/277891341): Remove this and related usages. This has been replaced by
+ // InsetsSource#FLAG_FORCE_CONSUMING.
public boolean areSystemBarsForcedConsumedLw() {
- return mForceConsumeSystemBars;
- }
-
- /**
- * @return true if the system bars are forced to stay visible
- */
- public boolean areSystemBarsForcedShownLw() {
- return mForceShowSystemBars;
+ return false;
}
/**
@@ -1694,7 +1684,8 @@ public class DisplayPolicy {
* @return Whether the top fullscreen app hides the given type of system bar.
*/
boolean topAppHidesSystemBar(@InsetsType int type) {
- if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) {
+ if (mTopFullscreenOpaqueWindowState == null
+ || getInsetsPolicy().areTypesForciblyShowing(type)) {
return false;
}
return !mTopFullscreenOpaqueWindowState.isRequestedVisible(type);
@@ -2371,14 +2362,7 @@ public class DisplayPolicy {
final boolean freeformRootTaskVisible =
defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- // We need to force showing system bars when adjacent tasks or freeform roots visible.
- mForceShowSystemBars = adjacentTasksVisible || freeformRootTaskVisible;
- // We need to force the consumption of the system bars if they are force shown or if they
- // are controlled by a remote insets controller.
- mForceConsumeSystemBars = mForceShowSystemBars
- || getInsetsPolicy().remoteInsetsControllerControlsSystemBars(win)
- || getInsetsPolicy().forcesShowingNavigationBars(win);
- mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
+ getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible, freeformRootTaskVisible);
final boolean topAppHidesStatusBar = topAppHidesSystemBar(Type.statusBars());
if (getStatusBar() != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b681c198538f..99cbdde306a2 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -977,6 +977,8 @@ public class DisplayRotation {
return false;
case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
return true;
+ case IWindowManager.FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION:
+ return false;
default:
return mDefaultFixedToUserRotation;
}
@@ -1290,9 +1292,13 @@ public class DisplayRotation {
// Application just wants to remain locked in the last rotation.
preferredRotation = lastRotation;
} else if (!mSupportAutoRotation) {
- // If we don't support auto-rotation then bail out here and ignore
- // the sensor and any rotation lock settings.
- preferredRotation = -1;
+ if (mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION) {
+ preferredRotation = mUserRotation;
+ } else {
+ // If we don't support auto-rotation then bail out here and ignore
+ // the sensor and any rotation lock settings.
+ preferredRotation = -1;
+ }
} else if (((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
|| isTabletopAutoRotateOverrideEnabled())
&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 798dc85ec11b..835c92d0a30d 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -21,12 +21,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
-import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsSource.ID_IME;
-import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -39,16 +34,13 @@ import android.app.StatusBarManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.IBinder;
import android.util.SparseArray;
-import android.view.InsetsAnimationControlCallbacks;
-import android.view.InsetsAnimationControlImpl;
-import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
-import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InternalInsetsAnimationController;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.WindowInsets;
@@ -56,14 +48,16 @@ import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
-import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.DisplayThread;
import com.android.server.statusbar.StatusBarManagerInternal;
+import java.io.PrintWriter;
+import java.util.List;
+
/**
* Policy that implements who gets control over the windows generating insets.
*/
@@ -77,47 +71,19 @@ class InsetsPolicy {
private final DisplayContent mDisplayContent;
private final DisplayPolicy mPolicy;
- /** For resetting visibilities of insets sources. */
- private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
+ /** Used to show system bars transiently. This won't affect the layout. */
+ private final InsetsControlTarget mTransientControlTarget;
- @Override
- public void notifyInsetsControlChanged() {
- boolean hasLeash = false;
- final InsetsSourceControl[] controls =
- mStateController.getControlsForDispatch(this);
- if (controls == null) {
- return;
- }
- for (InsetsSourceControl control : controls) {
- if (isTransient(control.getType())) {
- // The visibilities of transient bars will be handled with animations.
- continue;
- }
- final SurfaceControl leash = control.getLeash();
- if (leash != null) {
- hasLeash = true;
-
- // We use alpha to control the visibility here which aligns the logic at
- // SurfaceAnimator.createAnimationLeash
- final boolean visible =
- (control.getType() & WindowInsets.Type.defaultVisible()) != 0;
- mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f);
- }
- }
- if (hasLeash) {
- mDisplayContent.scheduleAnimation();
- }
- }
- };
+ /** Used to show system bars permanently. This will affect the layout. */
+ private final InsetsControlTarget mPermanentControlTarget;
private WindowState mFocusedWin;
private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private @InsetsType int mShowingTransientTypes;
- private boolean mAnimatingShown;
+ private @InsetsType int mForcedShowingTypes;
private final boolean mHideNavBarForKeyboard;
- private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
@@ -125,9 +91,12 @@ class InsetsPolicy {
mPolicy = displayContent.getDisplayPolicy();
final Resources r = mPolicy.getContext().getResources();
mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
+ mTransientControlTarget = new ControlTarget(
+ stateController, displayContent.mWmService.mH, "TransientControlTarget");
+ mPermanentControlTarget = new ControlTarget(
+ stateController, displayContent.mWmService.mH, "PermanentControlTarget");
}
-
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
if (mFocusedWin != focusedWin) {
@@ -142,13 +111,13 @@ class InsetsPolicy {
final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
mStateController.onBarControlTargetChanged(
statusControlTarget,
- statusControlTarget == mDummyControlTarget
+ statusControlTarget == mTransientControlTarget
? getStatusControlTarget(focusedWin, true /* fake */)
: statusControlTarget == notificationShade
? getStatusControlTarget(topApp, true /* fake */)
: null,
navControlTarget,
- navControlTarget == mDummyControlTarget
+ navControlTarget == mTransientControlTarget
? getNavControlTarget(focusedWin, true /* fake */)
: navControlTarget == notificationShade
? getNavControlTarget(topApp, true /* fake */)
@@ -198,19 +167,19 @@ class InsetsPolicy {
mFocusedWin,
(showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0,
isGestureOnSystemBar);
-
- // The leashes can be created while updating bar control target. The surface transaction
- // of the new leashes might not be applied yet. The callback posted here ensures we can
- // get the valid leashes because the surface transaction will be applied in the next
- // animation frame which will be triggered if a new leash is created.
- mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
- synchronized (mDisplayContent.mWmService.mGlobalLock) {
- startAnimation(true /* show */, null /* callback */);
- }
- });
}
}
+ @VisibleForTesting
+ InsetsControlTarget getTransientControlTarget() {
+ return mTransientControlTarget;
+ }
+
+ @VisibleForTesting
+ InsetsControlTarget getPermanentControlTarget() {
+ return mPermanentControlTarget;
+ }
+
void hideTransient() {
if (mShowingTransientTypes == 0) {
return;
@@ -221,23 +190,8 @@ class InsetsPolicy {
/* areVisible= */ false,
/* wereRevealedFromSwipeOnSystemBar= */ false);
- startAnimation(false /* show */, () -> {
- synchronized (mDisplayContent.mWmService.mGlobalLock) {
- final SparseArray<InsetsSourceProvider> providers =
- mStateController.getSourceProviders();
- for (int i = providers.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = providers.valueAt(i);
- if (!isTransient(provider.getSource().getType())) {
- continue;
- }
- // We are about to clear mShowingTransientTypes, we don't want the transient bar
- // can cause insets on the client. Restore the client visibility.
- provider.setClientVisible(false);
- }
- mShowingTransientTypes = 0;
- updateBarControlTarget(mFocusedWin);
- }
- });
+ mShowingTransientTypes = 0;
+ updateBarControlTarget(mFocusedWin);
}
boolean isTransient(@InsetsType int type) {
@@ -500,7 +454,7 @@ class InsetsPolicy {
private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
boolean fake) {
if (!fake && isTransient(Type.statusBars())) {
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
final WindowState notificationShade = mPolicy.getNotificationShade();
if (focusedWin == notificationShade) {
@@ -514,16 +468,16 @@ class InsetsPolicy {
component, focusedWin.getRequestedVisibleTypes());
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (mPolicy.areSystemBarsForcedShownLw()) {
+ if (areTypesForciblyShowing(Type.statusBars())) {
// Status bar is forcibly shown. We don't want the client to control the status bar, and
// we will dispatch the real visibility of status bar to the client.
- return null;
+ return mPermanentControlTarget;
}
if (forceShowsStatusBarTransiently() && !fake) {
// Status bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
if (!canBeTopFullscreenOpaqueWindow(focusedWin)
&& mPolicy.topAppHidesSystemBar(Type.statusBars())
@@ -554,7 +508,7 @@ class InsetsPolicy {
return null;
}
if (!fake && isTransient(Type.navigationBars())) {
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
if (focusedWin == mPolicy.getNotificationShade()) {
// Notification shade has control anyways, no reason to force anything.
@@ -567,13 +521,6 @@ class InsetsPolicy {
return focusedWin;
}
}
- if (forcesShowingNavigationBars(focusedWin)) {
- // When "force show navigation bar" is enabled, it means both force visible is true, and
- // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
- // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
- // still control the navigation bar in this mode.
- return null;
- }
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
ComponentName component = focusedWin.mActivityRecord != null
? focusedWin.mActivityRecord.mActivityComponent : null;
@@ -581,16 +528,16 @@ class InsetsPolicy {
component, focusedWin.getRequestedVisibleTypes());
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (mPolicy.areSystemBarsForcedShownLw()) {
+ if (areTypesForciblyShowing(Type.navigationBars())) {
// Navigation bar is forcibly shown. We don't want the client to control the navigation
// bar, and we will dispatch the real visibility of navigation bar to the client.
- return null;
+ return mPermanentControlTarget;
}
if (forceShowsNavigationBarTransiently() && !fake) {
// Navigation bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
- return mDummyControlTarget;
+ return mTransientControlTarget;
}
final WindowState notificationShade = mPolicy.getNotificationShade();
if (!canBeTopFullscreenOpaqueWindow(focusedWin)
@@ -603,7 +550,32 @@ class InsetsPolicy {
return focusedWin;
}
- boolean forcesShowingNavigationBars(WindowState win) {
+ boolean areTypesForciblyShowing(@InsetsType int types) {
+ return (mForcedShowingTypes & types) == types;
+ }
+
+ void updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode) {
+ mForcedShowingTypes = (inSplitScreenMode || inFreeformMode)
+ ? (Type.statusBars() | Type.navigationBars())
+ : forceShowingNavigationBars(win)
+ ? Type.navigationBars()
+ : 0;
+
+ // The client app won't be able to control these types of system bars. Here makes the client
+ // forcibly consume these types to prevent the app content from getting obscured.
+ mStateController.setForcedConsumingTypes(
+ mForcedShowingTypes | (remoteInsetsControllerControlsSystemBars(win)
+ ? (Type.statusBars() | Type.navigationBars())
+ : 0));
+
+ updateBarControlTarget(win);
+ }
+
+ private boolean forceShowingNavigationBars(WindowState win) {
+ // When "force show navigation bar" is enabled, it means both force visible is true, and
+ // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
+ // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
+ // still control the navigation bar in this mode.
return mPolicy.isForceShowNavigationBarEnabled() && win != null
&& win.getActivityType() == ACTIVITY_TYPE_STANDARD;
}
@@ -642,34 +614,6 @@ class InsetsPolicy {
&& (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
}
- @VisibleForTesting
- void startAnimation(boolean show, Runnable callback) {
- @InsetsType int typesReady = 0;
- final SparseArray<InsetsSourceControl> controlsReady = new SparseArray<>();
- final InsetsSourceControl[] controls =
- mStateController.getControlsForDispatch(mDummyControlTarget);
- if (controls == null) {
- if (callback != null) {
- DisplayThread.getHandler().post(callback);
- }
- return;
- }
- for (InsetsSourceControl control : controls) {
- if (isTransient(control.getType()) && control.getLeash() != null) {
- typesReady |= control.getType();
- controlsReady.put(control.getId(), new InsetsSourceControl(control));
- }
- }
- controlAnimationUnchecked(typesReady, controlsReady, show, callback);
- }
-
- private void controlAnimationUnchecked(int typesReady,
- SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
- InsetsPolicyAnimationControlListener listener =
- new InsetsPolicyAnimationControlListener(show, callback, typesReady);
- listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
- }
-
private void dispatchTransientSystemBarsVisibilityChanged(
@Nullable WindowState focusedWindow,
boolean areVisible,
@@ -696,6 +640,21 @@ class InsetsPolicy {
wereRevealedFromSwipeOnSystemBar);
}
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "InsetsPolicy");
+ prefix = prefix + " ";
+ pw.println(prefix + "status: " + StatusBarManager.windowStateToString(mStatusBar.mState));
+ pw.println(prefix + "nav: " + StatusBarManager.windowStateToString(mNavBar.mState));
+ if (mShowingTransientTypes != 0) {
+ pw.println(prefix + "mShowingTransientTypes="
+ + WindowInsets.Type.toString(mShowingTransientTypes));
+ }
+ if (mForcedShowingTypes != 0) {
+ pw.println(prefix + "mForcedShowingTypes="
+ + WindowInsets.Type.toString(mForcedShowingTypes));
+ }
+ }
+
private class BarWindow {
private final int mId;
@@ -725,105 +684,138 @@ class InsetsPolicy {
}
}
- private class InsetsPolicyAnimationControlListener extends
- InsetsController.InternalAnimationControlListener {
- Runnable mFinishCallback;
- InsetsPolicyAnimationControlCallbacks mControlCallbacks;
+ private static class ControlTarget implements InsetsControlTarget {
+
+ private final InsetsState mState = new InsetsState();
+ private final InsetsController mInsetsController;
+ private final InsetsStateController mStateController;
+ private final String mName;
- InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
- super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
- false /* disable */, 0 /* floatingImeBottomInsets */,
- null /* loggingListener */, null /* jankContext */);
- mFinishCallback = finishCallback;
- mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
+ ControlTarget(InsetsStateController stateController, Handler handler, String name) {
+ mStateController = stateController;
+ mInsetsController = new InsetsController(new Host(handler, name));
+ mName = name;
}
@Override
- protected void onAnimationFinish() {
- super.onAnimationFinish();
- if (mFinishCallback != null) {
- DisplayThread.getHandler().post(mFinishCallback);
- }
+ public void notifyInsetsControlChanged() {
+ mState.set(mStateController.getRawInsetsState(), true /* copySources */);
+ mInsetsController.onStateChanged(mState);
+ mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
}
- private class InsetsPolicyAnimationControlCallbacks implements
- InsetsAnimationControlCallbacks {
- private InsetsAnimationControlImpl mAnimationControl = null;
- private InsetsPolicyAnimationControlListener mListener;
+ @Override
+ public String toString() {
+ return mName;
+ }
+ }
- InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
- mListener = listener;
- }
+ private static class Host implements InsetsController.Host {
- private void controlAnimationUnchecked(int typesReady,
- SparseArray<InsetsSourceControl> controls, boolean show) {
- if (typesReady == 0) {
- // nothing to animate.
- return;
- }
- mAnimatingShown = show;
-
- final InsetsState state = mFocusedWin.getInsetsState();
-
- // We are about to playing the default animation. Passing a null frame indicates
- // the controlled types should be animated regardless of the frame.
- mAnimationControl = new InsetsAnimationControlImpl(controls,
- null /* frame */, state, mListener, typesReady, this,
- mListener.getDurationMs(), getInsetsInterpolator(),
- show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
- ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
- : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- null /* translator */, null /* statsToken */);
- SurfaceAnimationThread.getHandler().post(
- () -> mListener.onReady(mAnimationControl, typesReady));
- }
+ private final float[] mTmpFloat9 = new float[9];
+ private final Handler mHandler;
+ private final String mName;
- /** Called on SurfaceAnimationThread without global WM lock held. */
- @Override
- public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
- if (mAnimationControl.applyChangeInsets(null /* outState */)) {
- mAnimationControl.finish(mAnimatingShown);
- }
- }
+ Host(Handler handler, String name) {
+ mHandler = handler;
+ mName = name;
+ }
- @Override
- public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
- // Nothing's needed here. Finish steps is handled in the listener
- // onAnimationFinished callback.
- }
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
- /** Called on SurfaceAnimationThread without global WM lock held. */
- @Override
- public void applySurfaceParams(
- final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
- applyParams(t, surfaceParams, mTmpFloat9);
- }
- t.apply();
- t.close();
- }
+ @Override
+ public void notifyInsetsChanged() { }
- // Since we don't push applySurfaceParams to a Handler-queue we don't need
- // to push release in this case.
- @Override
- public void releaseSurfaceControlFromRt(SurfaceControl sc) {
- sc.release();
- }
+ @Override
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull WindowInsetsAnimation animation) { }
- @Override
- public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
- void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
- WindowInsetsAnimation animation,
- Bounds bounds) {
- }
+ @Override
+ public Bounds dispatchWindowInsetsAnimationStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull Bounds bounds) {
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets dispatchWindowInsetsAnimationProgress(
+ @NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationEnd(
+ @NonNull WindowInsetsAnimation animation) { }
- @Override
- public void reportPerceptible(int types, boolean perceptible) {
- // No-op for now - only client windows report perceptibility for now, with policy
- // controllers assumed to always be perceptible.
+ @Override
+ public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = p.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9);
}
+ t.apply();
+ t.close();
+ }
+
+ @Override
+ public void updateRequestedVisibleTypes(int types) { }
+
+ @Override
+ public boolean hasAnimationCallbacks() {
+ return false;
+ }
+
+ @Override
+ public void setSystemBarsAppearance(int appearance, int mask) { }
+
+ @Override
+ public int getSystemBarsAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setSystemBarsBehavior(int behavior) { }
+
+ @Override
+ public int getSystemBarsBehavior() {
+ return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ }
+
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
+ surfaceControl.release();
+ }
+
+ @Override
+ public void addOnPreDrawRunnable(Runnable r) { }
+
+ @Override
+ public void postInsetsAnimationCallback(Runnable r) { }
+
+ @Override
+ public InputMethodManager getInputMethodManager() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getRootViewTitle() {
+ return mName;
+ }
+
+ @Override
+ public int dipToPx(int dips) {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public IBinder getWindowToken() {
+ return null;
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e1c865bb85be..2b8312c3ea60 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -40,6 +40,7 @@ import android.graphics.Rect;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
+import android.view.InsetsSource.Flags;
import android.view.InsetsSourceControl;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -81,6 +82,8 @@ class InsetsSourceProvider {
private final Rect mSourceFrame = new Rect();
private final Rect mLastSourceFrame = new Rect();
private @NonNull Insets mInsetsHint = Insets.NONE;
+ private @Flags int mFlagsFromFrameProvider;
+ private @Flags int mFlagsFromServer;
private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
if (mControl != null) {
@@ -189,6 +192,16 @@ class InsetsSourceProvider {
}
}
+ boolean setFlags(@Flags int flags, @Flags int mask) {
+ mFlagsFromServer = (mFlagsFromServer & ~mask) | (flags & mask);
+ final @Flags int mergedFlags = mFlagsFromFrameProvider | mFlagsFromServer;
+ if (mSource.getFlags() != mergedFlags) {
+ mSource.setFlags(mergedFlags);
+ return true;
+ }
+ return false;
+ }
+
/**
* The source frame can affect the layout of other windows, so this should be called once the
* window container gets laid out.
@@ -217,11 +230,11 @@ class InsetsSourceProvider {
mSourceFrame.set(frame);
if (mFrameProvider != null) {
- final int flags = mFrameProvider.apply(
+ mFlagsFromFrameProvider = mFrameProvider.apply(
mWindowContainer.getDisplayContent().mDisplayFrames,
mWindowContainer,
mSourceFrame);
- mSource.setFlags(flags);
+ mSource.setFlags(mFlagsFromFrameProvider | mFlagsFromServer);
}
updateSourceFrameForServerVisibility();
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index addb5219c663..081ebe0e7cbd 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.ID_IME;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
@@ -85,6 +86,8 @@ class InsetsStateController {
}
};
+ private @InsetsType int mForcedConsumingTypes;
+
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
}
@@ -122,6 +125,11 @@ class InsetsStateController {
provider = id == ID_IME
? new ImeInsetsSourceProvider(source, this, mDisplayContent)
: new InsetsSourceProvider(source, this, mDisplayContent);
+ provider.setFlags(
+ (mForcedConsumingTypes & type) != 0
+ ? FLAG_FORCE_CONSUMING
+ : 0,
+ FLAG_FORCE_CONSUMING);
mProviders.put(id, provider);
return provider;
}
@@ -137,6 +145,24 @@ class InsetsStateController {
}
}
+ void setForcedConsumingTypes(@InsetsType int types) {
+ if (mForcedConsumingTypes != types) {
+ mForcedConsumingTypes = types;
+ boolean changed = false;
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ final InsetsSourceProvider provider = mProviders.valueAt(i);
+ changed |= provider.setFlags(
+ (types & provider.getSource().getType()) != 0
+ ? FLAG_FORCE_CONSUMING
+ : 0,
+ FLAG_FORCE_CONSUMING);
+ }
+ if (changed) {
+ notifyInsetsChanged();
+ }
+ }
+ }
+
/**
* Called when a layout pass has occurred.
*/
@@ -391,6 +417,10 @@ class InsetsStateController {
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).dump(pw, prefix + " ");
}
+ if (mForcedConsumingTypes != 0) {
+ pw.println(prefix + "mForcedConsumingTypes="
+ + WindowInsets.Type.toString(mForcedConsumingTypes));
+ }
}
void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 671400cc92a7..d03388a8455e 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -204,7 +204,8 @@ class KeyguardController {
// handle the snapshot.
// - The display state is ON. Because if AOD is not on or pulsing, the display state will
// be OFF or DOZE (the path of screen off may have handled it).
- if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
+ if (displayId == DEFAULT_DISPLAY
+ && ((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
&& !state.mKeyguardGoingAway && Display.isOnState(
mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
@@ -418,13 +419,17 @@ class KeyguardController {
return;
}
- final boolean waitAppTransition = isKeyguardLocked(displayId);
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
- waitAppTransition);
- if (waitAppTransition) {
- mService.deferWindowLayout();
- try {
- if (isDisplayOccluded(DEFAULT_DISPLAY)) {
+ final TransitionController tc = mRootWindowContainer.mTransitionController;
+
+ final boolean occluded = isDisplayOccluded(displayId);
+ final boolean performTransition = isKeyguardLocked(displayId);
+ final boolean executeTransition = performTransition && !tc.isCollecting();
+
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ mService.deferWindowLayout();
+ try {
+ if (isKeyguardLocked(displayId)) {
+ if (occluded) {
mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_FLAG_KEYGUARD_OCCLUDING,
@@ -434,11 +439,19 @@ class KeyguardController {
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_FLAG_KEYGUARD_UNOCCLUDING);
}
- updateKeyguardSleepToken(DEFAULT_DISPLAY);
+ } else {
+ if (tc.inTransition()) {
+ tc.mStateValidators.add(mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+ } else {
+ mWindowManager.mPolicy.applyKeyguardOcclusionChange();
+ }
+ }
+ updateKeyguardSleepToken(displayId);
+ if (performTransition && executeTransition) {
mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
}
+ } finally {
+ mService.continueWindowLayout();
}
}
@@ -485,6 +498,9 @@ class KeyguardController {
}
}
+ /**
+ * @return true if Keyguard is occluded or the device is dreaming.
+ */
boolean isDisplayOccluded(int displayId) {
return getDisplayState(displayId).mOccluded;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index adaecc2e6aa3..48420d22512e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1025,31 +1025,28 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* manager may choose to mirror or blank the display.
*/
boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
- final WindowManager.LayoutParams attrs = w.mAttrs;
- final int attrFlags = attrs.flags;
final boolean onScreen = w.isOnScreen();
- final boolean canBeSeen = w.isDisplayed();
- final int privateflags = attrs.privateFlags;
boolean displayHasContent = false;
ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
"handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
+ ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
w, w.mHasSurface, onScreen, w.isDisplayed(), w.mAttrs.userActivityTimeout);
- if (w.mHasSurface && onScreen) {
- if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
- mUserActivityTimeout = w.mAttrs.userActivityTimeout;
- ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "mUserActivityTimeout set to %d",
- mUserActivityTimeout);
- }
+ if (!onScreen) {
+ return false;
+ }
+ if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
+ mUserActivityTimeout = w.mAttrs.userActivityTimeout;
+ ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "mUserActivityTimeout set to %d",
+ mUserActivityTimeout);
}
- if (w.mHasSurface && canBeSeen) {
+ if (w.isDrawn() || (w.mActivityRecord != null && w.mActivityRecord.firstWindowDrawn
+ && w.mActivityRecord.isVisibleRequested())) {
if (!syswin && w.mAttrs.screenBrightness >= 0
&& Float.isNaN(mScreenBrightnessOverride)) {
mScreenBrightnessOverride = w.mAttrs.screenBrightness;
}
- final int type = attrs.type;
// This function assumes that the contents of the default display are processed first
// before secondary displays.
final DisplayContent displayContent = w.getDisplayContent();
@@ -1063,11 +1060,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
displayHasContent = true;
} else if (displayContent != null &&
(!mObscureApplicationContentOnSecondaryDisplays
- || (obscured && type == TYPE_KEYGUARD_DIALOG))) {
- // Allow full screen keyguard presentation dialogs to be seen.
+ || displayContent.isKeyguardAlwaysUnlocked()
+ || (obscured && w.mAttrs.type == TYPE_KEYGUARD_DIALOG))) {
+ // Allow full screen keyguard presentation dialogs to be seen, or simply ignore the
+ // keyguard if this display is always unlocked.
displayHasContent = true;
}
- if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
+ if ((w.mAttrs.privateFlags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
mSustainedPerformanceModeCurrent = true;
}
}
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 586077658a26..c914fa10687f 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -48,6 +48,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,6 +386,18 @@ public class SafeActivityOptions {
throw new SecurityException(msg);
}
+ // Check permission for remote transitions
+ final RemoteTransition transition = options.getRemoteTransition();
+ if (transition != null && supervisor.mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteTransition";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
// If launched from bubble is specified, then ensure that the caller is system or sysui.
if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 39772dda4792..9c23beb21a92 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3473,6 +3473,11 @@ class Task extends TaskFragment {
top.mLetterboxUiController.getLetterboxPositionForVerticalReachability();
}
}
+ // User Aspect Ratio Settings is enabled if the app is not in SCM
+ info.topActivityEligibleForUserAspectRatioButton =
+ mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && top != null && !info.topActivityInSizeCompat;
+ info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 29c192cc7c48..7e0e5a4cc599 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -549,8 +549,8 @@ public class TaskPersister implements PersisterQueue.Listener {
// Write out one task.
byte[] data = null;
Task task = mTask;
- if (DEBUG) Slog.d(TAG, "Writing task=" + task);
synchronized (mService.mGlobalLock) {
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
if (task.inRecents) {
// Still there.
try {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 79a54c3cfb32..1b27bb17f599 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -27,6 +27,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -77,6 +78,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -613,12 +615,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
if (mParticipants.contains(wc)) return;
- // Wallpaper is like in a static drawn state unless display may have changes, so exclude
- // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
- final boolean needSync = (!isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent))
- // Transient-hide may be hidden later, so no need to request redraw.
- && !isInTransientHide(wc);
- if (needSync) {
+ // Transient-hide may be hidden later, so no need to request redraw.
+ if (!isInTransientHide(wc)) {
mSyncEngine.addToSyncSet(mSyncId, wc);
}
ChangeInfo info = mChanges.get(wc);
@@ -1101,6 +1099,16 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final Task task = ar.getTask();
if (task == null) continue;
boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar);
+ // visibleAtTransitionEnd is used to guard against pre-maturely committing
+ // invisible on a window which is actually hidden by a later transition and not this
+ // one. However, for a transient launch, we can't use this mechanism because the
+ // visibility is determined at finish. Instead, use a different heuristic: don't
+ // commit invisible if the window is already in a later transition. That later
+ // transition will then handle the commit.
+ if (isTransientLaunch(ar) && !ar.isVisibleRequested()
+ && mController.inCollectingTransition(ar)) {
+ visibleAtTransitionEnd = true;
+ }
// We need both the expected visibility AND current requested-visibility to be
// false. If it is expected-visible but not currently visible, it means that
// another animation is queued-up to animate this to invisibility, so we can't
@@ -2262,7 +2270,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// transitions anyways).
return wc.getParent().asDisplayContent().getWindowingLayer();
}
- return wc.getParent().getSurfaceControl();
+ return wc.getParentSurfaceControl();
}
/**
@@ -2657,7 +2665,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
private void validateKeyguardOcclusion() {
- if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+ if ((mFlags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
mController.mStateValidators.add(
mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
}
@@ -2686,6 +2694,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
});
}
+ /**
+ * Returns {@code true} if the transition and the corresponding transaction should be applied
+ * on display thread. Currently, this only checks for display rotation change because the order
+ * of dispatching the new display info will be after requesting the windows to sync drawing.
+ * That avoids potential flickering of screen overlays (e.g. cutout, rounded corner). Also,
+ * because the display thread has a higher priority, it is faster to perform the configuration
+ * changes and window hierarchy traversal.
+ */
+ boolean shouldApplyOnDisplayThread() {
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final DisplayContent dc = mParticipants.valueAt(i).asDisplayContent();
+ if (dc == null) continue;
+ final ChangeInfo changeInfo = mChanges.get(dc);
+ if (changeInfo != null && changeInfo.mRotation != dc.getRotation()) {
+ return Looper.myLooper() != mController.mAtm.mWindowManager.mH.getLooper();
+ }
+ }
+ return false;
+ }
+
/** Applies the new configuration for the changed displays. */
void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index a539a4893d4f..79cb61be5948 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -588,15 +588,6 @@ class TransitionController {
/** Sets the sync method for the display change. */
private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
@NonNull Transition displayTransition, @NonNull DisplayContent displayContent) {
- final int startRotation = displayChange.getStartRotation();
- final int endRotation = displayChange.getEndRotation();
- if (startRotation != endRotation && (startRotation + endRotation) % 2 == 0) {
- // 180 degrees rotation change may not change screen size. So the clients may draw
- // some frames before and after the display projection transaction is applied by the
- // remote player. That may cause some buffers to show in different rotation. So use
- // sync method to pause clients drawing until the projection transaction is applied.
- mSyncEngine.setSyncMethod(displayTransition.getSyncId(), BLASTSyncEngine.METHOD_BLAST);
- }
final Rect startBounds = displayChange.getStartAbsBounds();
final Rect endBounds = displayChange.getEndAbsBounds();
if (startBounds == null || endBounds == null) return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9e7df004806b..805e7ffe7d76 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@ import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Message;
import android.util.Pair;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -515,12 +516,13 @@ public abstract class WindowManagerInternal {
* Invalidate all visible windows on a given display, and report back on the callback when all
* windows have redrawn.
*
- * @param callback reporting callback to be called when all windows have redrawn.
+ * @param message The message will be sent when all windows have redrawn. Note that the message
+ * must be obtained from handler, otherwise it will throw NPE.
* @param timeout calls the callback anyway after the timeout.
* @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all
* windows on all displays.
*/
- public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId);
+ public abstract void waitForAllWindowsDrawn(Message message, long timeout, int displayId);
/**
* Overrides the display size.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6f4f075f1428..cb1c08645458 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -601,7 +601,7 @@ public class WindowManagerService extends IWindowManager.Stub
* The callbacks to make when the windows all have been drawn for a given
* {@link WindowContainer}.
*/
- final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>();
+ final ArrayMap<WindowContainer<?>, Message> mWaitingForDrawnCallbacks = new ArrayMap<>();
/** List of window currently causing non-system overlay windows to be hidden. */
private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
@@ -5359,8 +5359,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
- public static final int ALL_WINDOWS_DRAWN = 33;
-
public static final int NEW_ANIMATOR_SCALE = 34;
public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;
@@ -5482,7 +5480,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
case WAITING_FOR_DRAWN_TIMEOUT: {
- Runnable callback = null;
+ final Message callback;
final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
synchronized (mGlobalLock) {
ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
@@ -5496,7 +5494,7 @@ public class WindowManagerService extends IWindowManager.Stub
callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
- callback.run();
+ callback.sendToTarget();
}
break;
}
@@ -5520,17 +5518,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
break;
}
- case ALL_WINDOWS_DRAWN: {
- Runnable callback;
- final WindowContainer container = (WindowContainer) msg.obj;
- synchronized (mGlobalLock) {
- callback = mWaitingForDrawnCallbacks.remove(container);
- }
- if (callback != null) {
- callback.run();
- }
- break;
- }
case NEW_ANIMATOR_SCALE: {
float scale = getCurrentAnimatorScale();
ValueAnimator.setDurationScale(scale);
@@ -6078,7 +6065,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mWaitingForDrawnCallbacks.isEmpty()) {
return;
}
- mWaitingForDrawnCallbacks.forEach((container, callback) -> {
+ for (int i = mWaitingForDrawnCallbacks.size() - 1; i >= 0; i--) {
+ final WindowContainer<?> container = mWaitingForDrawnCallbacks.keyAt(i);
for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
ProtoLog.i(WM_DEBUG_SCREEN_ON,
@@ -6104,9 +6092,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (container.mWaitingForDrawn.isEmpty()) {
ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
- mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container));
+ mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
}
- });
+ }
}
private void traceStartWaitingForWindowDrawn(WindowState window) {
@@ -7818,13 +7806,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
+ public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) {
+ Objects.requireNonNull(message.getTarget());
final WindowContainer<?> container = displayId == INVALID_DISPLAY
? mRoot : mRoot.getDisplayContent(displayId);
if (container == null) {
// The waiting container doesn't exist, no need to wait to run the callback. Run and
// return;
- callback.run();
+ message.sendToTarget();
return;
}
boolean allWindowsDrawn = false;
@@ -7841,13 +7830,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mWaitingForDrawnCallbacks.put(container, callback);
+ mWaitingForDrawnCallbacks.put(container, message);
mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
}
}
if (allWindowsDrawn) {
- callback.run();
+ message.sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 7b64fed3e116..32d574a0fa10 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -321,9 +321,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
return transition.getToken();
}
- transition.start();
transition.mLogger.mStartWCT = wct;
- applyTransaction(wct, -1 /*syncId*/, transition, caller);
+ if (transition.shouldApplyOnDisplayThread()) {
+ mService.mH.post(() -> {
+ synchronized (mService.mGlobalLock) {
+ transition.start();
+ applyTransaction(wct, -1 /* syncId */, transition, caller);
+ }
+ });
+ } else {
+ transition.start();
+ applyTransaction(wct, -1 /* syncId */, transition, caller);
+ }
// Since the transition is already provided, it means WMCore is determining the
// "readiness lifecycle" outside the provided transaction, so don't set ready here.
return transition.getToken();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf1e51fb4e94..0d4c2d631b2c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5665,7 +5665,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
if (mIsWallpaper) {
// TODO(b/233286785): Add sync support to wallpaper.
- return false;
+ return true;
}
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 31afcbf26220..9806be85467b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -388,12 +388,23 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
SurfaceControl.Builder makeSurface() {
final SurfaceControl.Builder builder = super.makeSurface();
+ // The overlay may use COLOR_MODE_A8 that needs to be at the top of the display to avoid
+ // additional memory usage, see b/235601833. Note that getParentSurfaceControl() must use
+ // the same parent.
if (mRoundedCornerOverlay) {
builder.setParent(null);
}
return builder;
}
+ @Override
+ public SurfaceControl getParentSurfaceControl() {
+ if (mRoundedCornerOverlay) {
+ return null;
+ }
+ return super.getParentSurfaceControl();
+ }
+
boolean isClientVisible() {
return mClientVisible;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index cb0b9c9ace4f..c74c727d8cdf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -968,7 +968,7 @@ void NativeInputManager::notifyDropWindow(const sp<IBinder>& token, float x, flo
void NativeInputManager::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) {
static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
- sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
+ sysprop::InputProperties::enable_input_device_usage_metrics().value_or(false);
if (!ENABLE_INPUT_DEVICE_USAGE_METRICS) return;
mInputManager->getMetricsCollector().notifyDeviceInteraction(deviceId, timestamp, uids);
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 46c90b4bc731..bafa4a56b28a 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -126,6 +126,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
protected ProviderRegistryGetSession(@NonNull Context context,
@@ -143,6 +144,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
private List<Entry> prepareUiCredentialEntries(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6ea71e382a71..2f1d67d34c43 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3469,8 +3469,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
revertTransferOwnershipIfNecessaryLocked();
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
}
- updateUsbDataSignal();
// In case flag value has changed, we apply it during boot to avoid doing it concurrently
// with user toggling quiet mode.
@@ -16224,6 +16226,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
+ return DevicePolicyManagerService.this.getDeviceOwnerComponent(callingUserOnly);
+ }
+
+ @Override
public int getDeviceOwnerUserId() {
return DevicePolicyManagerService.this.getDeviceOwnerUserId();
}
@@ -19500,8 +19507,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public boolean isCurrentInputMethodSetByOwner() {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwner(caller) || isSystemUid(caller),
- "Only profile owner, device owner and system may call this method.");
+ || isProfileOwner(caller) || canQueryAdminPolicy(caller) || isSystemUid(caller),
+ "Only profile owner, device owner, a caller with QUERY_ADMIN_POLICY "
+ + "permission or system may call this method.");
return getUserData(caller.getUserId()).mCurrentInputMethodSet;
}
@@ -22355,7 +22363,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setUsbDataSignalingEnabled(String packageName, boolean enabled) {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- if (!isPermissionCheckFlagEnabled()) {
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"USB data signaling can only be controlled by a device owner or "
@@ -22364,22 +22372,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
"USB data signaling cannot be disabled.");
}
-
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
+ if (isPolicyEngineForFinanceFlagEnabled()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
caller.getPackageName(),
- caller.getUserId()).getActiveAdmin();
+ caller.getUserId());
+ Preconditions.checkState(canUsbDataSignalingBeDisabled(),
+ "USB data signaling cannot be disabled.");
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ enforcingAdmin,
+ new BooleanPolicyValue(enabled));
} else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
-
- if (admin.mUsbDataSignalingEnabled != enabled) {
- admin.mUsbDataSignalingEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
- updateUsbDataSignal();
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ if (admin.mUsbDataSignalingEnabled != enabled) {
+ admin.mUsbDataSignalingEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
}
}
DevicePolicyEventLogger
@@ -22389,16 +22400,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.write();
}
- private void updateUsbDataSignal() {
- if (!canUsbDataSignalingBeDisabled()) {
+ static void updateUsbDataSignal(Context context, boolean value) {
+ if (!canUsbDataSignalingBeDisabledInternal(context)) {
return;
}
- final boolean usbEnabled;
- synchronized (getLockObject()) {
- usbEnabled = isUsbDataSignalingEnabledInternalLocked();
- }
- if (!mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getUsbManager().enableUsbDataSignal(usbEnabled))) {
+ if (!Binder.withCleanCallingIdentity(
+ () -> context.getSystemService(UsbManager.class).enableUsbDataSignal(value))) {
Slogf.w(LOG_TAG, "Failed to set usb data signaling state");
}
}
@@ -22406,28 +22413,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- synchronized (getLockObject()) {
- // If the caller is an admin, return the policy set by itself. Otherwise
- // return the device-wide policy.
- if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
- return getProfileOwnerOrDeviceOwnerLocked(
- caller.getUserId()).mUsbDataSignalingEnabled;
- } else {
- return isUsbDataSignalingEnabledInternalLocked();
+ if (isPolicyEngineForFinanceFlagEnabled()) {
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
+ } else {
+ synchronized (getLockObject()) {
+ // If the caller is an admin, return the policy set by itself. Otherwise
+ // return the device-wide policy.
+ if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
+ caller)) {
+ return getProfileOwnerOrDeviceOwnerLocked(
+ caller.getUserId()).mUsbDataSignalingEnabled;
+ } else {
+ return isUsbDataSignalingEnabledInternalLocked();
+ }
}
}
}
- @Override
- public boolean isUsbDataSignalingEnabledForUser(int userId) {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isSystemUid(caller));
-
- synchronized (getLockObject()) {
- return isUsbDataSignalingEnabledInternalLocked();
- }
- }
-
private boolean isUsbDataSignalingEnabledInternalLocked() {
// TODO(b/261999445): remove
ActiveAdmin admin;
@@ -22442,9 +22447,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean canUsbDataSignalingBeDisabled() {
- return mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getUsbManager() != null
- && mInjector.getUsbManager().getUsbHalVersion() >= UsbManager.USB_HAL_V1_3
+ return canUsbDataSignalingBeDisabledInternal(mContext);
+ }
+
+ private static boolean canUsbDataSignalingBeDisabledInternal(Context context) {
+ return Binder.withCleanCallingIdentity(() ->
+ context.getSystemService(UsbManager.class) != null
+ && context.getSystemService(UsbManager.class).getUsbHalVersion()
+ >= UsbManager.USB_HAL_V1_3
);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 7e48407fc911..7a877b9afdad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -332,6 +332,14 @@ final class PolicyDefinition<V> {
PolicyEnforcerCallbacks::setPersonalAppsSuspended,
new BooleanPolicySerializer());
+ static PolicyDefinition<Boolean> USB_DATA_SIGNALING = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY),
+ // usb data signaling is enabled by default, hence disabling it is more restrictive.
+ FALSE_MORE_RESTRICTIVE,
+ POLICY_FLAG_GLOBAL_ONLY_POLICY,
+ (Boolean value, Context context, Integer userId, PolicyKey policyKey) ->
+ PolicyEnforcerCallbacks.setUsbDataSignalingEnabled(value, context),
+ new BooleanPolicySerializer());
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -364,6 +372,8 @@ final class PolicyDefinition<V> {
SCREEN_CAPTURE_DISABLED);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERSONAL_APPS_SUSPENDED_POLICY,
PERSONAL_APPS_SUSPENDED);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY,
+ USB_DATA_SIGNALING);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 3b048b250075..6570ce1cd500 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -302,4 +302,14 @@ final class PolicyEnforcerCallbacks {
Slogf.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
}
}
+
+ static boolean setUsbDataSignalingEnabled(@Nullable Boolean value, @NonNull Context context) {
+ return Binder.withCleanCallingIdentity(() -> {
+ Objects.requireNonNull(context);
+
+ boolean enabled = value == null || value;
+ DevicePolicyManagerService.updateUsbDataSignal(context, enabled);
+ return true;
+ });
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d1bf4fc2e6bc..2f2e1cbaef5e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -337,6 +337,8 @@ public final class SystemServer implements Dumpable {
"com.android.clockwork.time.WearTimeService";
private static final String WEAR_SETTINGS_SERVICE_CLASS =
"com.android.clockwork.settings.WearSettingsService";
+ private static final String WRIST_ORIENTATION_SERVICE_CLASS =
+ "com.android.clockwork.wristorientation.WristOrientationService";
private static final String ACCOUNT_SERVICE_CLASS =
"com.android.server.accounts.AccountManagerService$Lifecycle";
private static final String CONTENT_SERVICE_CLASS =
@@ -1557,9 +1559,11 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
t.traceEnd();
- t.traceBegin("StartVibratorManagerService");
- mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
- t.traceEnd();
+ if (!isTv) {
+ t.traceBegin("StartVibratorManagerService");
+ mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
+ t.traceEnd();
+ }
t.traceBegin("StartDynamicSystemService");
dynamicSystem = new DynamicSystemService(context);
@@ -2582,7 +2586,7 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(MediaProjectionManagerService.class);
t.traceEnd();
- if (isWatch) {
+ if (isWatch) {
// Must be started before services that depend it, e.g. WearConnectivityService
t.traceBegin("StartWearPowerService");
mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS);
@@ -2615,6 +2619,14 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartWearModeService");
mSystemServiceManager.startService(WEAR_MODE_SERVICE_CLASS);
t.traceEnd();
+
+ boolean enableWristOrientationService = SystemProperties.getBoolean(
+ "config.enable_wristorientation", false);
+ if (enableWristOrientationService) {
+ t.traceBegin("StartWristOrientationService");
+ mSystemServiceManager.startService(WRIST_ORIENTATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
}
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
@@ -2714,10 +2726,12 @@ public final class SystemServer implements Dumpable {
Slog.d(TAG, "TranslationService not defined by OEM");
}
- // Selection toolbar service
- t.traceBegin("StartSelectionToolbarManagerService");
- mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS);
- t.traceEnd();
+ if (!isTv) {
+ // Selection toolbar service
+ t.traceBegin("StartSelectionToolbarManagerService");
+ mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+ }
// NOTE: ClipboardService depends on ContentCapture and Autofill
t.traceBegin("StartClipboardService");
diff --git a/services/tests/InputMethodSystemServerTests/OWNERS b/services/tests/InputMethodSystemServerTests/OWNERS
index 1f2c036773a4..eb4fe3c12036 100644
--- a/services/tests/InputMethodSystemServerTests/OWNERS
+++ b/services/tests/InputMethodSystemServerTests/OWNERS
@@ -1 +1,2 @@
+# Bug component: 34867
include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index a6ada4d77253..869497c28def 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -20,6 +20,7 @@ import static android.inputmethodservice.InputMethodService.IME_ACTIVE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
@@ -43,6 +44,7 @@ import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
@@ -165,6 +167,29 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
}
+ @Test
+ public void testApplyImeVisibility_hideImeWhenUnbinding() {
+ mInputMethodManagerService.setAttachedClientForTesting(null);
+ startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ ExtendedMockito.spyOn(mVisibilityApplier);
+
+ synchronized (ImfLock.class) {
+ // Simulate the system hides the IME when switching IME services in different users.
+ // (e.g. unbinding the IME from the current user to the profile user)
+ final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null,
+ HIDE_SWITCH_USER);
+ mInputMethodManagerService.onUnbindCurrentMethodByReset();
+
+ // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing
+ // the IME hidden state.
+ verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(),
+ eq(STATE_HIDE_IME));
+ verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
+ eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ }
+ }
+
private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
return mInputMethodManagerService.startInputOrWindowGainedFocus(
StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index e33ca7775e22..b7a0cf389396 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,6 +138,14 @@ public class CrossUserPackageVisibilityTests {
}
@Test
+ public void testGetUserMinAspectRatio_withCrossUserId() {
+ final int crossUserId = UserHandle.myUserId() + 1;
+ assertThrows(SecurityException.class,
+ () -> mIPackageManager.getUserMinAspectRatio(
+ mInstrumentation.getContext().getPackageName(), crossUserId));
+ }
+
+ @Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
index 30bb6478d18d..6feebb8c833c 100644
--- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
+++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 435f0d7f5f15..a805e5c9f87e 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -20,6 +20,7 @@ import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static java.lang.reflect.Modifier.isFinal;
@@ -138,7 +139,7 @@ public class PackageManagerServiceTest {
.setSecondaryCpuAbiString("secondaryCpuAbiString")
.setCpuAbiOverrideString("cpuAbiOverrideString")
.build();
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, 4, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -150,7 +151,7 @@ public class PackageManagerServiceTest {
pri.mBroadcastUsers = null;
final int EXCLUDED_USER_ID = 4;
setting.setInstantApp(true, EXCLUDED_USER_ID);
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, EXCLUDED_USER_ID, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -164,8 +165,8 @@ public class PackageManagerServiceTest {
@Test
public void testPartitions() {
- String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
- String[] appdir = { "app", "priv-app" };
+ String[] partitions = {"system", "vendor", "odm", "oem", "product", "system_ext"};
+ String[] appdir = {"app", "priv-app"};
for (int i = 0; i < partitions.length; i++) {
final ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
@@ -425,10 +426,10 @@ public class PackageManagerServiceTest {
private String displayName(Method m) {
String r = m.getName();
String p = Arrays.toString(m.getGenericParameterTypes())
- .replaceAll("([a-zA-Z0-9]+\\.)+", "")
- .replace("class ", "")
- .replaceAll("^\\[", "(")
- .replaceAll("\\]$", ")");
+ .replaceAll("([a-zA-Z0-9]+\\.)+", "")
+ .replace("class ", "")
+ .replaceAll("^\\[", "(")
+ .replaceAll("\\]$", ")");
return r + p;
}
@@ -612,4 +613,22 @@ public class PackageManagerServiceTest {
runShellCommand("pm uninstall " + TEST_PKG_NAME);
}
}
+
+ @Test
+ public void testSetUserMinAspectRatio_samePackage_succeeds() throws Exception {
+ mIPackageManager.setUserMinAspectRatio(PACKAGE_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ // Invoking setUserMinAspectRatio on the same package shouldn't get any exception.
+ }
+
+ @Test
+ public void testSetUserMinAspectRatio_differentPackage_fails() {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ runShellCommand("pm install " + testApk);
+ assertThrows(SecurityException.class, () -> {
+ mIPackageManager.setUserMinAspectRatio(TEST_PKG_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ });
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 836f8581e8eb..16fb012b2f40 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -971,7 +971,7 @@ public class PackageManagerSettingsTests {
origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
- "splashScreenTheme", 1000L);
+ "splashScreenTheme", 1000L, PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
final PersistableBundle appExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = createPersistableBundle(
@@ -1638,7 +1638,8 @@ public class PackageManagerSettingsTests {
: oldUserState.getSharedLibraryOverlayPaths() == null)
&& userState.getSplashScreenTheme().equals(
oldUserState.getSplashScreenTheme())
- && userState.getUninstallReason() == oldUserState.getUninstallReason();
+ && userState.getUninstallReason() == oldUserState.getUninstallReason()
+ && userState.getMinAspectRatio() == oldUserState.getMinAspectRatio();
}
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index b242ec2db420..f1ff33809184 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -22,11 +22,20 @@ android_test {
"src/**/*.java",
],
+ libs: [
+ "android.test.mock",
+ ],
+
static_libs: [
- "services.core",
- "androidx.test.runner",
- "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "display-core-libs",
+ "frameworks-base-testutils",
+ "junit",
+ "junit-params",
+ "platform-compat-test-rules",
"platform-test-annotations",
+ "services.core",
+ "servicestests-utils",
],
defaults: [
@@ -47,3 +56,10 @@ android_test {
enabled: false,
},
}
+
+java_library {
+ name: "display-core-libs",
+ srcs: [
+ "src/com/android/server/display/TestUtils.java",
+ ],
+}
diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml
index c2e417429eac..d2bd10dd18dc 100644
--- a/services/tests/displayservicetests/AndroidManifest.xml
+++ b/services/tests/displayservicetests/AndroidManifest.xml
@@ -21,6 +21,16 @@
Insert permissions here. eg:
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-->
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true"
android:testOnly="true">
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
index 2c4fe536b75c..7333bc75fe9d 100644
--- a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index a6acd60f3bd7..a6acd60f3bd7 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 5f81869903c3..ee7826f13578 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.display;
@@ -206,11 +206,11 @@ public class BrightnessMappingStrategyTest {
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
- final int N = DISPLAY_LEVELS_BACKLIGHT.length;
+ final int n = DISPLAY_LEVELS_BACKLIGHT.length;
final float expectedBrightness =
- (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON;
+ (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
assertEquals(expectedBrightness,
- strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
+ strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@Test
@@ -270,10 +270,10 @@ public class BrightnessMappingStrategyTest {
// Check that null returns us to the default configuration.
strategy.setBrightnessConfiguration(null);
- final int N = DISPLAY_LEVELS_NITS.length;
- final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1];
+ final int n = DISPLAY_LEVELS_NITS.length;
+ final float expectedBrightness = DISPLAY_LEVELS_NITS[n - 1] / DISPLAY_RANGE_NITS[1];
assertEquals(expectedBrightness,
- strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
+ strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 46956d74cc5c..8faaf5998d13 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -159,7 +159,7 @@ public class BrightnessThrottlerTest {
@Test
public void testThermalThrottlingSingleLevel() throws Exception {
final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
- 0.25f);
+ 0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(level);
@@ -184,7 +184,7 @@ public class BrightnessThrottlerTest {
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status more than high enough to trigger throttling
listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
@@ -192,7 +192,7 @@ public class BrightnessThrottlerTest {
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to the lower throttling level
listener.notifyThrottling(getSkinTemp(level.thermalStatus));
@@ -200,7 +200,7 @@ public class BrightnessThrottlerTest {
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
@@ -208,15 +208,15 @@ public class BrightnessThrottlerTest {
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
}
@Test
public void testThermalThrottlingMultiLevel() throws Exception {
final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
- 0.62f);
+ 0.62f);
final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
- 0.25f);
+ 0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(levelLo);
@@ -242,7 +242,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
@@ -250,7 +250,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
@@ -258,7 +258,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to exceed the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
@@ -266,7 +266,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
@@ -274,7 +274,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to the lowest configured throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
@@ -282,7 +282,7 @@ public class BrightnessThrottlerTest {
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
index 021f2d1df835..44c7dec7633e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.display;
@@ -395,8 +395,8 @@ public class BrightnessTrackerTest {
final long currentTime = mInjector.currentTimeMillis();
notifyBrightnessChanged(mTracker, brightness, displayId, new float[] {1000.0f},
new long[] {TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos())});
- List<BrightnessChangeEvent> eventsNoPackage
- = mTracker.getEvents(0, false).getList();
+ List<BrightnessChangeEvent> eventsNoPackage =
+ mTracker.getEvents(0, false).getList();
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
mTracker.stop();
@@ -1037,9 +1037,9 @@ public class BrightnessTrackerTest {
}
void setBrightnessMode(boolean isBrightnessModeAutomatic) {
- mIsBrightnessModeAutomatic = isBrightnessModeAutomatic;
- mContentObserver.dispatchChange(false, null);
- waitForHandler();
+ mIsBrightnessModeAutomatic = isBrightnessModeAutomatic;
+ mContentObserver.dispatchChange(false, null);
+ waitForHandler();
}
void sendScreenChange(boolean screenOn) {
@@ -1184,8 +1184,8 @@ public class BrightnessTrackerTest {
@Override
public int getNightDisplayColorTemperature(Context context) {
- return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
- mDefaultNightModeColorTemperature);
+ return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ mDefaultNightModeColorTemperature);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
index 53d8de0c2bbb..53d8de0c2bbb 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
index 130e6ad91b49..130e6ad91b49 100644
--- a/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 8b04eca69132..8b04eca69132 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5db9d1f6f5bd..d16c9c59bb1b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -142,7 +142,7 @@ public class DisplayManagerServiceTest {
private static final float FLOAT_TOLERANCE = 0.01f;
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
- private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+ private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
@@ -238,7 +238,7 @@ public class DisplayManagerServiceTest {
boolean getHdrOutputConversionSupport() {
return true;
}
- }
+ }
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/HbmEventTest.java
index 24fc34849829..24fc34849829 100644
--- a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HbmEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index e2a66f03f5ca..76e6ec7f6780 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -483,8 +483,10 @@ public class HighBrightnessModeControllerTest {
// Verify Stats HBM_ON_HDR
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/,
0, 0, 0 /*flags*/, 1.0f /*maxDesiredHdrSdrRatio*/);
@@ -492,8 +494,10 @@ public class HighBrightnessModeControllerTest {
// Verify Stats HBM_OFF
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
@@ -501,16 +505,20 @@ public class HighBrightnessModeControllerTest {
// Verify Stats HBM_ON_SUNLIGHT
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.onAmbientLuxChange(1);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1);
// Verify Stats HBM_OFF
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
}
@Test
@@ -527,8 +535,8 @@ public class HighBrightnessModeControllerTest {
// Verify Stats HBM_ON_HDR not report
verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- anyInt());
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ anyInt());
}
@Test
@@ -545,8 +553,8 @@ public class HighBrightnessModeControllerTest {
// Verify Stats HBM_ON_SUNLIGHT not report
verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- anyInt());
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ anyInt());
}
// Test reporting of thermal throttling when triggered externally through
@@ -565,8 +573,10 @@ public class HighBrightnessModeControllerTest {
BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
advanceTime(1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
// Brightness is thermally throttled, HBM brightness denied (NBM brightness granted)
hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness,
@@ -578,8 +588,8 @@ public class HighBrightnessModeControllerTest {
// the HBM transition point.
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
}
@Test
@@ -592,15 +602,17 @@ public class HighBrightnessModeControllerTest {
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
// Use up all the time in the window.
advanceTime(TIME_WINDOW_MILLIS + 1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
}
@Test
@@ -613,13 +625,17 @@ public class HighBrightnessModeControllerTest {
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
}
@Test
@@ -632,16 +648,18 @@ public class HighBrightnessModeControllerTest {
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/, 1.0f /*maxDesiredHdrSdrRatio*/);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
}
@Test
@@ -657,15 +675,17 @@ public class HighBrightnessModeControllerTest {
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
// verify HBM_ON_SUNLIGHT
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
// verify HBM_SV_OFF due to LOW_REQUESTED_BRIGHTNESS
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog
- .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
}
private void assertState(HighBrightnessModeController hbmc,
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
index ede54e096ad0..ede54e096ad0 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 1eec70da3d20..1eec70da3d20 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 30ff8ba6e331..20654797a5d2 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -40,6 +40,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.display.layout.Layout;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.io.InputStream;
@@ -121,7 +122,9 @@ public class LogicalDisplayTest {
assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
}
+ // TODO: b/288880734 - fix test after display tests migration
@Test
+ @Ignore
public void testDisplayInputFlags() {
SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
diff --git a/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
index c379d6b79ee7..c379d6b79ee7 100644
--- a/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 642f54c25a46..9f91916a4046 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
index 5b10dc4e0bab..5b10dc4e0bab 100644
--- a/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java
index 4494b0c412dc..4494b0c412dc 100644
--- a/services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java
diff --git a/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
new file mode 100644
index 000000000000..8b45145b160f
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.input.InputSensorInfo;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.view.DisplayAddress;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class TestUtils {
+
+ public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(1);
+ event.sensor = sensor;
+ event.values[0] = value;
+ event.timestamp = SystemClock.elapsedRealtimeNanos();
+ return event;
+ }
+
+
+ public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ }
+
+ public static void setMaximumRange(Sensor sensor, float maximumRange) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setRange", Float.TYPE, Float.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, maximumRange, 1);
+ }
+
+ public static Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ return sensor;
+ }
+
+ public static Sensor createSensor(int type, String strType, float maximumRange)
+ throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ setMaximumRange(sensor, maximumRange);
+ return sensor;
+ }
+
+ public static Sensor createSensor(String type, String name) {
+ return new Sensor(new InputSensorInfo(
+ name, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1,
+ type, "", 0, 0, 0));
+ }
+
+ /**
+ * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
+ * display-address implementation in our code. Intentionally uses default object (reference)
+ * equality rules.
+ */
+ public static class TestDisplayAddress extends DisplayAddress {
+ @Override
+ public void writeToParcel(Parcel out, int flags) { }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index c0c63c69add8..c0c63c69add8 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index e58b3e891b70..e58b3e891b70 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c4f483810478..c4f483810478 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index a9e616d766c6..a9e616d766c6 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index b652576a75c8..b652576a75c8 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index c4346317a6ef..c4346317a6ef 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index d60caf6efb7a..d60caf6efb7a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 081f19d19f75..081f19d19f75 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index 530245dacd8b..530245dacd8b 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index 7147aa8d3701..7147aa8d3701 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index 9830edbea645..9830edbea645 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/AppSaturationControllerTest.java
index a525814435ea..a525814435ea 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/AppSaturationControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/CctEvaluatorTest.java
index b96666ae40a3..b96666ae40a3 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/CctEvaluatorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index 618ab1b75587..c7c09b5deb35 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -1093,15 +1093,15 @@ public class ColorDisplayServiceTest {
@Test
public void compositionColorSpaces_invalidResources() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL,
- // Missing second color mode
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ // Missing second color mode
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- Display.COLOR_MODE_DISPLAY_P3
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ Display.COLOR_MODE_DISPLAY_P3
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1111,13 +1111,13 @@ public class ColorDisplayServiceTest {
@Test
public void compositionColorSpaces_validResources_validColorMode() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1127,13 +1127,13 @@ public class ColorDisplayServiceTest {
@Test
public void compositionColorSpaces_validResources_invalidColorMode() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1143,7 +1143,7 @@ public class ColorDisplayServiceTest {
@Test
public void getColorMode_noAvailableModes_returnsNotSet() {
when(mResourcesSpy.getIntArray(R.array.config_availableColorModes))
- .thenReturn(new int[] {});
+ .thenReturn(new int[] {});
startService();
verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt());
assertThat(mBinderService.getColorMode()).isEqualTo(-1);
diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index e0bef1a83821..e0bef1a83821 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
index 4f0cb324f17f..4f0cb324f17f 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
index 35014dcb7492..35014dcb7492 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 04273d6f4ed6..04273d6f4ed6 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index 9ab6ee5bd230..9ab6ee5bd230 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 287fdd5c344b..287fdd5c344b 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/AmbientFilterTest.java
index 9b76b13d2ede..9b76b13d2ede 100644
--- a/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/utils/AmbientFilterTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
index 4d2551087c59..4d2551087c59 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index ac97911027bf..f975b6fd1d6f 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -268,9 +268,9 @@ public final class AmbientLuxTest {
controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
}
}
@@ -286,9 +286,9 @@ public final class AmbientLuxTest {
controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
}
}
@@ -366,22 +366,22 @@ public final class AmbientLuxTest {
@Test
public void testSpline_InvalidCombinations() throws Exception {
- setBrightnesses(100.0f, 200.0f);
- setBiases(0.0f, 1.0f);
- setHighLightBrightnesses(150.0f, 250.0f);
- setHighLightBiases(0.0f, 1.0f);
-
- DisplayWhiteBalanceController controller =
- DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
- final float ambientColorTemperature = 8000.0f;
- setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(new AmbientFilterStubber());
-
- for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
- }
+ setBrightnesses(100.0f, 200.0f);
+ setBiases(0.0f, 1.0f);
+ setHighLightBrightnesses(150.0f, 250.0f);
+ setHighLightBiases(0.0f, 1.0f);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 8000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
+
+ for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
+ }
}
@Test
@@ -486,7 +486,7 @@ public final class AmbientLuxTest {
private void mockResourcesFloat(int id, float floatValue) {
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
- TypedValue value = (TypedValue)invocation.getArgument(1);
+ TypedValue value = (TypedValue) invocation.getArgument(1);
value.type = TypedValue.TYPE_FLOAT;
value.data = Float.floatToIntBits(floatValue);
return null;
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
index 3e3e535df986..3e3e535df986 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 32243f04f6e8..212a243c6a9e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -2221,7 +2221,7 @@ public class GameManagerServiceTests {
String[] packages = {mPackageName};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
}
@@ -2238,12 +2238,12 @@ public class GameManagerServiceTests {
doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
.when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
assertTrue(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
assertTrue(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
@@ -2260,13 +2260,13 @@ public class GameManagerServiceTests {
int somePackageId = DEFAULT_PACKAGE_UID + 1;
when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
}
@@ -2277,9 +2277,9 @@ public class GameManagerServiceTests {
String[] packages = {mPackageName};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 434a75f0e218..b5f03bab291b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1086,6 +1086,16 @@ public final class DisplayPowerController2Test {
.getThermalBrightnessThrottlingDataMapByThrottlingId();
}
+ @Test
+ public void testSetBrightness_BrightnessShouldBeClamped() {
+ float clampedBrightness = 0.6f;
+ when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness);
+
+ mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX);
+
+ verify(mHolder.brightnessSetting).setBrightness(clampedBrightness);
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index db786bd00658..f3e3d342776b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1092,6 +1092,16 @@ public final class DisplayPowerControllerTest {
.getThermalBrightnessThrottlingDataMapByThrottlingId();
}
+ @Test
+ public void testSetBrightness_BrightnessShouldBeClamped() {
+ float clampedBrightness = 0.6f;
+ when(mHolder.hbmController.getCurrentBrightnessMax()).thenReturn(clampedBrightness);
+
+ mHolder.dpc.setBrightness(PowerManager.BRIGHTNESS_MAX);
+
+ verify(mHolder.brightnessSetting).setBrightness(clampedBrightness);
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f89f73c98cfd..aa0a2fea1a5a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1257,6 +1257,17 @@ public class LocalDisplayAdapterTest {
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
}
+
+ // Instead of using DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay)
+ // we should use DisplayDeviceConfig.create(context, isFirstDisplay) for the test to ensure
+ // that real device DisplayDeviceConfig is not loaded for FakeDisplay and we are getting
+ // consistent behaviour. Please also note that context passed to this method, is
+ // mMockContext and values will be loaded from mMockResources.
+ @Override
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay);
+ }
}
private class TestListener implements DisplayAdapter.Listener {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index c197b3419211..d6a4d40c763c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -16,9 +16,13 @@
package com.android.server.pm
+import android.Manifest.permission.CONTROL_KEYGUARD
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_DENIED
+import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.pm.UserInfo
import android.os.Build
+import android.os.UserHandle.USER_SYSTEM
import android.util.Log
import com.android.server.testutils.any
import com.android.server.testutils.spy
@@ -105,6 +109,8 @@ class DeletePackageHelperTest {
whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(
UserInfo(1, "test", UserInfo.FLAG_ADMIN))
+ whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
+ .thenReturn(PERMISSION_DENIED)
val dph = DeletePackageHelper(mPms)
val result = dph.deletePackageX("a.data.package", 1L, 1,
@@ -124,6 +130,8 @@ class DeletePackageHelperTest {
whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
UserInfo(userId, "testparent", UserInfo.FLAG_ADMIN))
+ whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
+ .thenReturn(PERMISSION_DENIED)
val dph = DeletePackageHelper(mPms)
val result = dph.deletePackageX("a.data.package", 1L, userId,
@@ -138,6 +146,9 @@ class DeletePackageHelperTest {
whenever(PackageManagerServiceUtils.isUpdatedSystemApp(ps)).thenReturn(true)
whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
+ whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
+ whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
+ .thenReturn(PERMISSION_DENIED)
val dph = DeletePackageHelper(mPms)
val result = dph.deletePackageX("a.data.package", 1L, 1,
@@ -145,4 +156,18 @@ class DeletePackageHelperTest {
assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
}
+
+ @Test
+ fun deleteSystemPackageWithKeyguard_fails() {
+ val ps = mPms.mSettings.getPackageLPr("a.data.package")
+ whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
+ whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
+ .thenReturn(PERMISSION_GRANTED)
+
+ val dph = DeletePackageHelper(mPms)
+ val result = dph.deletePackageX("a.data.package", 1L, 1,
+ PackageManager.DELETE_SYSTEM_APP, false)
+
+ assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_INTERNAL_ERROR)
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
new file mode 100644
index 000000000000..96985521049f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+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 android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+
+/**
+ * A unit test for PackageMonitorCallbackHelper implementation.
+ */
+@RunWith(JUnit4.class)
+public class PackageMonitorCallbackHelperTest {
+
+ private static final String FAKE_PACKAGE_NAME = "com.android.server.pm.fakeapp";
+ private static final int FAKE_PACKAGE_UID = 123;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+
+ @Rule
+ public final MockSystemRule mMockSystem = new MockSystemRule();
+
+ @Before
+ public void setup() {
+ when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler);
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(
+ mMockSystem.mocks().getInjector());
+ }
+
+
+ @After
+ public void teardown() {
+ mPackageMonitorCallbackHelper = null;
+ }
+
+ @Test
+ public void testWithoutRegisterPackageMonitorCallback_callbackNotCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
+
+ verify(callback, never()).sendResult(any());
+ }
+
+ @Test
+ public void testUnregisterPackageMonitorCallback_callbackShouldNotCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0});
+ Thread.sleep(300);
+
+ verify(callback, times(1)).sendResult(any());
+
+ reset(callback);
+ mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
+
+ verify(callback, never()).sendResult(any());
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallback_callbackCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */);
+ Thread.sleep(300);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(callback, times(1)).sendResult(bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_PACKAGE_ADDED);
+ assertThat(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)).isEqualTo(0);
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallbackUserNotMatch_callbackShouldNotCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ // Register for user 0
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ // Notify for user 10
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */);
+ Thread.sleep(300);
+
+ verify(callback, never()).sendResult(any());
+ }
+
+ @Test
+ public void testNotifyPackageChanged_callbackCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ ArrayList<String> components = new ArrayList<>();
+ String component1 = FAKE_PACKAGE_NAME + "/.Component1";
+ components.add(component1);
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME,
+ false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */,
+ new int[]{0} /* userIds */);
+ Thread.sleep(300);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(callback, times(1)).sendResult(bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_PACKAGE_CHANGED);
+ assertThat(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)).isEqualTo(0);
+ assertThat(intent.getStringExtra(Intent.EXTRA_REASON)).isNull();
+ assertThat(intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, true)).isFalse();
+ assertThat(intent.getIntExtra(Intent.EXTRA_UID, -1)).isEqualTo(FAKE_PACKAGE_UID);
+ String[] result = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ assertThat(result).isNotNull();
+ assertThat(result.length).isEqualTo(1);
+ assertThat(result[0]).isEqualTo(component1);
+ }
+
+ @Test
+ public void testNotifyPackageAddedForNewUsers_callbackCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME,
+ FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0],
+ PackageInstaller.DATA_LOADER_TYPE_STREAMING);
+ Thread.sleep(300);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(callback, times(1)).sendResult(bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_PACKAGE_ADDED);
+ assertThat(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)).isEqualTo(0);
+ assertThat(intent.getIntExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, -1)).isEqualTo(
+ PackageInstaller.DATA_LOADER_TYPE_STREAMING);
+ assertThat(intent.getIntExtra(Intent.EXTRA_UID, -1)).isEqualTo(FAKE_PACKAGE_UID);
+ }
+
+ @Test
+ public void testNotifyResourcesChanged_callbackCalled() throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */);
+ mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */,
+ true /* replacing */, new String[]{FAKE_PACKAGE_NAME},
+ new int[]{FAKE_PACKAGE_UID} /* uids */);
+ Thread.sleep(300);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(callback, times(1)).sendResult(bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ assertThat(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)).isEqualTo(0);
+ assertThat(intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)).isTrue();
+
+ int[] uids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+ assertThat(uids).isNotNull();
+ assertThat(uids.length).isEqualTo(1);
+ assertThat(uids[0]).isEqualTo(FAKE_PACKAGE_UID);
+
+ String[] pkgNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ assertThat(pkgNames).isNotNull();
+ assertThat(pkgNames.length).isEqualTo(1);
+ assertThat(pkgNames[0]).isEqualTo(FAKE_PACKAGE_NAME);
+ }
+
+ private IRemoteCallback createMockPackageMonitorCallback() {
+ return spy(new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ // no op
+ }
+ });
+ }
+
+ private Bundle createFakeBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ return bundle;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 7cd88196bf1b..60c15fe635ff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -325,6 +325,83 @@ public final class UserManagerServiceTest {
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
}
+ @Test
+ public void testGetPreviousFullUserToEnterForeground() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ assertWithMessage("getPreviousFullUserToEnterForeground")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(OTHER_USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsCurrentUser() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mockCurrentUser(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip current user")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsNonFullUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags &= ~UserInfo.FLAG_FULL;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip non-full users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsPartialUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.partial = true;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip partial users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsDisabledUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags |= UserInfo.FLAG_DISABLED;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip disabled users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsRemovingUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUms.addRemovingUserId(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip removing users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
private void mockCurrentUser(@UserIdInt int userId) {
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
index a3a49d7035d9..f3aa4274b3cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
@@ -259,6 +259,29 @@ public class AlarmQueueTest {
}
@Test
+ public void testMinTimeBetweenAlarms_freshAlarm() {
+ final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 5 * MINUTE_IN_MILLIS);
+ final long fixedTimeElapsed = mInjector.getElapsedRealtime();
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ final String pkg1 = "com.android.test.1";
+ final String pkg2 = "com.android.test.2";
+ alarmQueue.addAlarm(pkg1, fixedTimeElapsed + MINUTE_IN_MILLIS);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setExact(
+ anyInt(), eq(fixedTimeElapsed + MINUTE_IN_MILLIS), eq(ALARM_TAG), any(), any());
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ alarmQueue.onAlarm();
+ // Minimum of 5 minutes between alarms, so the next alarm should be 5 minutes after the
+ // first.
+ alarmQueue.addAlarm(pkg2, fixedTimeElapsed + 2 * MINUTE_IN_MILLIS);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setExact(
+ anyInt(), eq(fixedTimeElapsed + 6 * MINUTE_IN_MILLIS), eq(ALARM_TAG), any(), any());
+ }
+
+ @Test
public void testOnAlarm() {
final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 0);
final long nowElapsed = mInjector.getElapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index e6ef044d4791..5b5c8d415f84 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -202,6 +203,7 @@ public class FullScreenMagnificationControllerTest {
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
+ // Once for each display on unregister
verify(mMockThumbnail, times(2)).hideThumbnail();
}
@@ -543,7 +545,11 @@ public class FullScreenMagnificationControllerTest {
// The first time is triggered when the thumbnail is just created.
// The second time is triggered when the magnification region changed.
verify(mMockThumbnail, times(2)).setThumbnailBounds(
- any(), anyFloat(), anyFloat(), anyFloat());
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -681,6 +687,9 @@ public class FullScreenMagnificationControllerTest {
checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, displayId);
+
+ // Once on init before it's activated and once for reset
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -783,6 +792,9 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, DISPLAY_0);
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, DISPLAY_1);
+
+ // Twice for each display: once on init before it's activated and once for screen off
+ verify(mMockThumbnail, times(4)).hideThumbnail();
}
@Test
@@ -847,6 +859,15 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
checkActivatedAndMagnifying(
/* activated= */ expectedActivated, /* magnifying= */ false, displayId);
+
+ if (expectedActivated) {
+ verify(mMockThumbnail, times(2)).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
+ }
}
@Test
@@ -950,6 +971,13 @@ public class FullScreenMagnificationControllerTest {
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ eq(scale),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -984,6 +1012,13 @@ public class FullScreenMagnificationControllerTest {
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ eq(scale),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -1246,6 +1281,13 @@ public class FullScreenMagnificationControllerTest {
callbacks.onImeWindowVisibilityChanged(true);
mMessageCapturingHandler.sendAllMessages();
verify(mRequestObserver).onImeWindowVisibilityChanged(eq(DISPLAY_0), eq(true));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -1270,6 +1312,15 @@ public class FullScreenMagnificationControllerTest {
mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(false));
+ verify(mMockThumbnail).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
+
+ // Once on init before it's activated and once for reset
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -1281,6 +1332,12 @@ public class FullScreenMagnificationControllerTest {
assertEquals(1.0f, mFullScreenMagnificationController.getScale(DISPLAY_0), 0);
assertTrue(mFullScreenMagnificationController.isActivated(DISPLAY_0));
+ verify(mMockThumbnail).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
private void setScaleToMagnifying() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
index 3baa102b882b..8faddf8ff541 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
@@ -187,6 +187,29 @@ public class MagnificationThumbnailTest {
.addView(eq(mMagnificationThumbnail.mThumbnailLayout), any());
verify(mMockWindowManager, never())
.removeView(eq(mMagnificationThumbnail.mThumbnailLayout));
+ verify(mMockWindowManager, never())
+ .updateViewLayout(eq(mMagnificationThumbnail.mThumbnailLayout), any());
+ }
+
+ @Test
+ public void whenVisible_setBoundsUpdatesLayout() throws InterruptedException {
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
+ /* scale= */ 2f,
+ /* centerX= */ 5,
+ /* centerY= */ 10
+ ));
+ runOnMainSync(() -> mMagnificationThumbnail.setThumbnailBounds(
+ new Rect(),
+ /* scale= */ 2f,
+ /* centerX= */ 5,
+ /* centerY= */ 10
+ ));
+ idle();
+
+ verify(mMockWindowManager).updateViewLayout(
+ eq(mMagnificationThumbnail.mThumbnailLayout),
+ /* params= */ any()
+ );
}
private static void idle() {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index dccacb4d301a..24a628eb4331 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -1342,6 +1342,10 @@ public class UserControllerTest {
Message copy = new Message();
copy.copyFrom(msg);
mMessages.add(copy);
+ if (msg.getCallback() != null) {
+ msg.getCallback().run();
+ msg.setCallback(null);
+ }
return super.sendMessageAtTime(msg, uptimeMillis);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 99a3b808e082..ad1c60e2cc8a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -8125,14 +8125,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
- public void testIsUsbDataSignalingEnabledForUser_systemUser() throws Exception {
+ public void testIsUsbDataSignalingEnabledForUser() throws Exception {
when(getServices().usbManager.enableUsbDataSignal(false)).thenReturn(true);
when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
setDeviceOwner();
dpm.setUsbDataSignalingEnabled(false);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- assertThat(dpm.isUsbDataSignalingEnabledForUser(UserHandle.myUserId())).isFalse();
+ assertThat(dpm.isUsbDataSignalingEnabled()).isFalse();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 9ff600a6d0f8..e5fac7ac5e0c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -251,6 +251,8 @@ public class DpmMockContext extends MockContext {
return mMockSystemServices.roleManager;
case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
return mMockSystemServices.subscriptionManager;
+ case Context.USB_SERVICE:
+ return mMockSystemServices.usbManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/display/OWNERS b/services/tests/servicestests/src/com/android/server/display/OWNERS
deleted file mode 100644
index 6ce1ee4d3de2..000000000000
--- a/services/tests/servicestests/src/com/android/server/display/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/display/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
deleted file mode 100644
index 92d8abd4f173..000000000000
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "presubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {"include-filter": "com.android.server.display"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
- }
- ]
-}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index bc09d4b10723..399655ffac55 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -137,6 +137,20 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ /**
+ * Override displayOsd to prevent it from broadcasting an intent, which
+ * can trigger a SecurityException.
+ */
+ @Override
+ void displayOsd(int messageId) {
+ // do nothing
+ }
+
+ @Override
+ void displayOsd(int messageId, int extra) {
+ // do nothing
+ }
};
mLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 3bde665e87c0..cb19029d246e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -61,6 +61,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+ private boolean mIsCecControlEnabled = true;
@Override
public String nativeInit() {
@@ -128,7 +129,9 @@ final class FakeNativeWrapper implements NativeWrapper {
public void enableCec(boolean enabled) {}
@Override
- public void enableSystemCecControl(boolean enabled) {}
+ public void enableSystemCecControl(boolean enabled) {
+ mIsCecControlEnabled = enabled;
+ }
@Override
public void nativeSetLanguage(String language) {}
@@ -154,6 +157,10 @@ final class FakeNativeWrapper implements NativeWrapper {
mPortConnectionStatus.put(port, connected);
}
+ public boolean getIsCecControlEnabled() {
+ return mIsCecControlEnabled;
+ }
+
public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) {
mCecVersion = cecVersion;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
index 7c8a11ec1cea..04f921f495a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
@@ -24,10 +24,18 @@ import android.content.Context;
*/
final class FakePowerManagerWrapper extends PowerManagerWrapper {
private boolean mInteractive;
+ private WakeLockWrapper mWakeLock;
+ private boolean mWasWakeLockInstanceCreated = false;
+
FakePowerManagerWrapper(@NonNull Context context) {
+ this(context, null);
+ }
+
+ FakePowerManagerWrapper(@NonNull Context context, WakeLockWrapper wakeLock) {
super(context);
mInteractive = true;
+ mWakeLock = wakeLock;
}
@Override
@@ -51,5 +59,48 @@ final class FakePowerManagerWrapper extends PowerManagerWrapper {
return;
}
- // Don't stub WakeLock.
+ @Override
+ WakeLockWrapper newWakeLock(int levelAndFlags, String tag) {
+ if (mWakeLock == null) {
+ mWakeLock = new FakeWakeLockWrapper();
+ }
+ mWasWakeLockInstanceCreated = true;
+ return mWakeLock;
+ }
+
+ boolean wasWakeLockInstanceCreated() {
+ return mWasWakeLockInstanceCreated;
+ }
+
+ /**
+ * "Fake" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Default" wrapper used by
+ * the framework - see {@link PowerManagerWrapper.DefaultWakeLockWrapper}.
+ */
+ public static class FakeWakeLockWrapper implements WakeLockWrapper {
+ private static final String TAG = "FakeWakeLockWrapper";
+ private boolean mWakeLockHeld = false;
+
+ @Override
+ public void acquire(long timeout) {
+ mWakeLockHeld = true;
+ }
+
+ @Override
+ public void acquire() {
+ mWakeLockHeld = true;
+ }
+
+ @Override
+ public void release() {
+ mWakeLockHeld = false;
+ }
+
+ @Override
+ public boolean isHeld() {
+ return mWakeLockHeld;
+ }
+
+ @Override
+ public void setReferenceCounted(boolean value) {}
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 39930bc1e35a..0d172fdb2a80 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -21,6 +21,7 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
@@ -37,6 +38,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -63,6 +65,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.InOrder;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -87,6 +90,7 @@ public class HdmiControlServiceTest {
private HdmiEarcController mHdmiEarcController;
private FakeEarcNativeWrapper mEarcNativeWrapper;
private FakePowerManagerWrapper mPowerManager;
+ private WakeLockWrapper mWakeLockSpy;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@@ -164,7 +168,8 @@ public class HdmiControlServiceTest {
.build();
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlServiceSpy.initService();
- mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mWakeLockSpy = spy(new FakePowerManagerWrapper.FakeWakeLockWrapper());
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy, mWakeLockSpy);
mHdmiControlServiceSpy.setPowerManager(mPowerManager);
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiControlServiceSpy.setEarcSupported(true);
@@ -190,6 +195,7 @@ public class HdmiControlServiceTest {
doReturn(true).when(mHdmiControlServiceSpy).isStandbyMessageReceived();
mHdmiControlServiceSpy.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+
assertTrue(mPlaybackDeviceSpy.isStandby());
assertTrue(mAudioSystemDeviceSpy.isStandby());
assertTrue(mPlaybackDeviceSpy.isDisabled());
@@ -197,6 +203,75 @@ public class HdmiControlServiceTest {
}
@Test
+ public void playbackOnlyDevice_onStandbyCompleted_disableCecController() {
+ mLocalDevices.remove(mAudioSystemDeviceSpy);
+ mHdmiControlServiceSpy.clearCecLocalDevices();
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.disableCecLocalDevices(
+ new HdmiCecLocalDevice.PendingActionClearedCallback() {
+ @Override
+ public void onCleared(HdmiCecLocalDevice device) {
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.onPendingActionsCleared(
+ HdmiControlService.STANDBY_SCREEN_OFF);
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ verify(mPlaybackDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ assertFalse(mNativeWrapper.getIsCecControlEnabled());
+ }
+
+
+ @Test
+ public void playbackAndAudioDevice_onStandbyCompleted_doNotDisableCecController() {
+ mLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.disableCecLocalDevices(
+ new HdmiCecLocalDevice.PendingActionClearedCallback() {
+ @Override
+ public void onCleared(HdmiCecLocalDevice device) {
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ mHdmiControlServiceSpy.onPendingActionsCleared(
+ HdmiControlService.STANDBY_SCREEN_OFF);
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ verify(mPlaybackDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ verify(mAudioSystemDeviceSpy, times(1)).invokeStandbyCompletedCallback(any());
+ assertTrue(mNativeWrapper.getIsCecControlEnabled());
+ }
+
+ @Test
+ public void onStandby_acquireAndReleaseWakeLockSuccessfully() {
+ mHdmiControlServiceSpy.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ doReturn(true).when(mHdmiControlServiceSpy).isStandbyMessageReceived();
+ mTestLooper.dispatchAll();
+
+ assertFalse(mPowerManager.wasWakeLockInstanceCreated());
+ mHdmiControlServiceSpy.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+
+ InOrder inOrder = inOrder(mHdmiControlServiceSpy, mWakeLockSpy);
+ inOrder.verify(mWakeLockSpy, times(1)).acquire(DEVICE_CLEANUP_TIMEOUT);
+ inOrder.verify(mHdmiControlServiceSpy, times(1)).disableCecLocalDevices(any());
+ inOrder.verify(mHdmiControlServiceSpy, times(1))
+ .onPendingActionsCleared(HdmiControlService.STANDBY_SCREEN_OFF);
+ inOrder.verify(mWakeLockSpy, times(1)).release();
+
+ assertTrue(mPowerManager.wasWakeLockInstanceCreated());
+ }
+
+ @Test
public void initialPowerStatus_normalBoot_isTransientToStandby() {
assertThat(mHdmiControlServiceSpy.getInitialPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
@@ -903,6 +978,33 @@ public class HdmiControlServiceTest {
assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
}
+ @Test
+ public void multipleVendorCommandListeners_receiveCallback() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ VendorCommandListener secondVendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(secondVendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandNoId =
+ HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+ mNativeWrapper.onCecMessage(vendorCommandNoId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+
+ assertThat(secondVendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(secondVendorCmdListener.mParamsCorrect).isTrue();
+ }
+
private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
boolean mVendorCommandCallbackReceived = false;
boolean mParamsCorrect = false;
@@ -1423,8 +1525,10 @@ public class HdmiControlServiceTest {
}
@Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
mIsStandby = true;
+ invokeStandbyCompletedCallback(callback);
}
protected boolean isStandby() {
@@ -1476,8 +1580,10 @@ public class HdmiControlServiceTest {
}
@Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ protected void onStandby(boolean initiatedByCec, int standbyAction,
+ StandbyCompletedCallback callback) {
mIsStandby = true;
+ invokeStandbyCompletedCallback(callback);
}
protected boolean isStandby() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
index 079ef2e36673..024e36d62273 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java
@@ -262,4 +262,63 @@ public class TvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest {
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
+
+ @Test
+ public void adjustOnlyAvbEnabled_savlBecomesSupported_switchToAvb() {
+ enableAdjustOnlyAbsoluteVolumeBehavior();
+ mNativeWrapper.clearResultMessages();
+
+ // When the Audio System reports support for <Set Audio Volume Level>,
+ // the device should start the process for adopting AVB by sending <Give Audio Status>
+ receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
+ verifyGiveAudioStatusSent();
+
+ // The device should use adjust-only AVB while waiting for <Report Audio Status>
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+
+ // The device should switch to AVB upon receiving <Report Audio Status>
+ receiveReportAudioStatus(60, false);
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+
+ }
+
+ /**
+ * Tests that adjust-only AVB is not interrupted when a device's support for
+ * <Set Audio Volume Level> becomes unknown.
+ *
+ * This may currently occur when NewDeviceAction overwrites a device's info in HdmiCecNetwork.
+ * However, because replicating this scenario would be brittle and the behavior may change,
+ * this test does not simulate it and instead changes HdmiCecNetwork directly.
+ */
+ @Test
+ public void adjustOnlyAvbEnabled_savlSupportBecomesUnknown_keepUsingAdjustOnlyAvb() {
+ enableAdjustOnlyAbsoluteVolumeBehavior();
+ mNativeWrapper.clearResultMessages();
+
+ // Make sure the existing SetAudioVolumeLevelDiscoveryAction expires,
+ // so that we can check whether a new one is started.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS + 1);
+ mTestLooper.dispatchAll();
+
+ // Replace Audio System device info with one that has unknown support for all features
+ HdmiDeviceInfo updatedAudioSystemDeviceInfo =
+ mHdmiControlService.getHdmiCecNetwork().getDeviceInfo(Constants.ADDR_AUDIO_SYSTEM)
+ .toBuilder()
+ .setDeviceFeatures(DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN)
+ .build();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(updatedAudioSystemDeviceInfo);
+ mTestLooper.dispatchAll();
+
+ // The device should not switch away from adjust-only AVB
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+
+ // The device should query support for <Set Audio Volume Level> again
+ assertThat(mNativeWrapper.getResultMessages()).contains(
+ SetAudioVolumeLevelMessage.build(
+ getLogicalAddress(), getSystemAudioDeviceLogicalAddress(),
+ Constants.AUDIO_VOLUME_STATUS_UNKNOWN));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index 2f1a3760512b..498776db3ac8 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -133,8 +133,7 @@ class InputManagerServiceTests {
verify(native).setMotionClassifierEnabled(anyBoolean())
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
verify(native).setStylusPointerIconEnabled(anyBoolean())
- // TODO(b/286078544): There is no need to call this more than once.
- verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt())
+ verify(native).setKeyRepeatConfiguration(anyInt(), anyInt())
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/OWNERS b/services/tests/servicestests/src/com/android/server/inputmethod/OWNERS
index 5deb2ce8f24b..cbd94ba6b467 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/OWNERS
@@ -1 +1,2 @@
+# Bug component: 34867
include /core/java/android/view/inputmethod/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index c42928eba85f..bb8b986c6f61 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -38,6 +38,7 @@ 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 static org.testng.Assert.assertThrows;
import android.app.ActivityManagerInternal;
@@ -49,10 +50,14 @@ import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
+import android.media.projection.IMediaProjectionWatcherCallback;
import android.media.projection.ReviewGrantedConsentResult;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
@@ -86,6 +91,7 @@ public class MediaProjectionManagerServiceTest {
private static final int UID = 10;
private static final String PACKAGE_NAME = "test.package";
private final ApplicationInfo mAppInfo = new ApplicationInfo();
+ private final TestLooper mTestLooper = new TestLooper();
private static final ContentRecordingSession DISPLAY_SESSION =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
// Callback registered by an app on a MediaProjection instance.
@@ -110,6 +116,14 @@ public class MediaProjectionManagerServiceTest {
}
};
+ private final MediaProjectionManagerService.Injector mTestLooperInjector =
+ new MediaProjectionManagerService.Injector() {
+ @Override
+ Looper createCallbackLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+
private Context mContext;
private MediaProjectionManagerService mService;
private OffsettableClock mClock;
@@ -122,12 +136,15 @@ public class MediaProjectionManagerServiceTest {
private WindowManagerInternal mWindowManagerInternal;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private IMediaProjectionWatcherCallback mWatcherCallback;
@Captor
private ArgumentCaptor<ContentRecordingSession> mSessionCaptor;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mWatcherCallback.asBinder()).thenReturn(new Binder());
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
@@ -671,6 +688,59 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.isCurrentProjection(projection)).isTrue();
}
+ @Test
+ public void setContentRecordingSession_successful_notifiesListeners()
+ throws Exception {
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+
+ verify(mWatcherCallback).onRecordingSessionSet(
+ projection.getProjectionInfo(),
+ DISPLAY_SESSION
+ );
+ }
+
+ @Test
+ public void setContentRecordingSession_notifiesListenersOnCallbackLooper()
+ throws Exception {
+ mService = new MediaProjectionManagerService(mContext, mTestLooperInjector);
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+ doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+ // Callback not notified yet, as test looper hasn't dispatched the message yet
+ verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
+
+ mTestLooper.dispatchAll();
+ // Message dispatched on test looper. Callback should now be notified.
+ verify(mWatcherCallback).onRecordingSessionSet(
+ projection.getProjectionInfo(),
+ DISPLAY_SESSION
+ );
+ }
+
+ @Test
+ public void setContentRecordingSession_failure_doesNotNotifyListeners()
+ throws Exception {
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doReturn(false).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+
+ verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
+ }
+
private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) {
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
mSessionCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index fdf94bec8c18..39cc6537c759 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -182,7 +182,7 @@ public class UserManagerServiceCreateProfileTest {
UserInfo secondaryUser = addUser();
UserInfo profile = addProfile(secondaryUser);
// Add the profile it to the users being removed.
- mUserManagerService.addRemovingUserIdLocked(profile.id);
+ mUserManagerService.addRemovingUserId(profile.id);
// We should reuse the badge from the profile being removed.
assertEquals("Badge index not reused while removing a user", 0,
mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index 1f4c9f8cd343..b6fd65e5d3b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -111,7 +111,7 @@ public class UserManagerServiceIdRecyclingTest {
private void removeUser(int userId) {
mUserManagerService.removeUserInfo(userId);
- mUserManagerService.addRemovingUserIdLocked(userId);
+ mUserManagerService.addRemovingUserId(userId);
}
private void assertNoNextIdAvailable(String message) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 4e1196ff60c4..d94f10dfa7ea 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.app.ActivityManager;
import android.app.PropertyInvalidatedCache;
@@ -28,6 +29,7 @@ import android.os.Bundle;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
@@ -70,6 +72,9 @@ public class UserManagerServiceTest {
LocalServices.removeServiceForTest(UserManagerInternal.class);
mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
+ // Put the current user to mUsers. UMS can't find userlist.xml, and fallbackToSingleUserLP.
+ mUserManagerService.putUserInfo(
+ new UserInfo(ActivityManager.getCurrentUser(), "Current User", 0));
restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
restrictionsFile.delete();
@@ -304,8 +309,10 @@ public class UserManagerServiceTest {
private static String setSystemProperty(String name, String value) throws Exception {
final String oldValue = runShellCommand("getprop " + name);
- assertThat(runShellCommand("setprop " + name + " " + value))
- .isEqualTo("");
+ assertWithMessage("can not set system property")
+ .that(runShellCommand("setprop " + name + " " + value)).isEqualTo("");
+ assertWithMessage("failed to set system property")
+ .that(SystemProperties.get(name)).isEqualTo(value);
return oldValue;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7d0f36133a9c..dd681aa85c3f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -265,6 +265,52 @@ public final class UserManagerTest {
assertWithMessage("Communal profile not visible").that(umCommunal.isUserVisible()).isTrue();
}
+ @Test
+ public void testPrivateProfile() throws Exception {
+ UserHandle mainUser = mUserManager.getMainUser();
+ assumeTrue("Main user is null", mainUser != null);
+ // Get the default properties for private profile user type.
+ final UserTypeDetails userTypeDetails =
+ UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_PRIVATE);
+ assertWithMessage("No %s type on device", UserManager.USER_TYPE_PROFILE_PRIVATE)
+ .that(userTypeDetails).isNotNull();
+ final UserProperties typeProps = userTypeDetails.getDefaultUserPropertiesReference();
+
+ // Test that only one private profile can be created
+ final int mainUserId = mainUser.getIdentifier();
+ UserInfo userInfo = createProfileForUser("Private profile1",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mainUserId);
+ assertThat(userInfo).isNotNull();
+ UserInfo userInfo2 = createProfileForUser("Private profile2",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mainUserId);
+ assertThat(userInfo2).isNull();
+
+ // Check that the new private profile has the expected properties (relative to the defaults)
+ // provided that the test caller has the necessary permissions.
+ UserProperties privateProfileUserProperties =
+ mUserManager.getUserProperties(UserHandle.of(userInfo.id));
+ assertThat(typeProps.getShowInLauncher())
+ .isEqualTo(privateProfileUserProperties.getShowInLauncher());
+ assertThrows(SecurityException.class, privateProfileUserProperties::getStartWithParent);
+ assertThrows(SecurityException.class,
+ privateProfileUserProperties::getCrossProfileIntentFilterAccessControl);
+ assertThat(typeProps.isMediaSharedWithParent())
+ .isEqualTo(privateProfileUserProperties.isMediaSharedWithParent());
+ assertThat(typeProps.isCredentialShareableWithParent())
+ .isEqualTo(privateProfileUserProperties.isCredentialShareableWithParent());
+ assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
+
+ // Verify private profile parent
+ assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+ UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
+ assertThat(parentProfileInfo).isNotNull();
+ assertThat(mainUserId).isEqualTo(parentProfileInfo.id);
+ removeUser(userInfo.id);
+ assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+ }
+
@MediumTest
@Test
public void testAdd2Users() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
new file mode 100644
index 000000000000..003797066b39
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.parsing
+
+import android.content.res.Validator
+import android.os.Environment
+import android.platform.test.annotations.Postsubmit
+import com.android.internal.R
+import com.android.server.pm.PackageManagerService
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Assert.fail
+import org.junit.Test
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+import org.xmlpull.v1.XmlPullParserFactory
+import java.io.ByteArrayInputStream
+import java.io.File
+
+@Postsubmit
+class AndroidPackageParsingValidationTest {
+ companion object {
+ private val parser2 = PackageParser2.forParsingFileWithDefaults()
+ private val apks = ((PackageManagerService.SYSTEM_PARTITIONS)
+ .flatMap {
+ listOfNotNull(it.privAppFolder, it.appFolder, it.overlayFolder)
+ } + File(Environment.getRootDirectory(), "framework"))
+ .flatMap {
+ it.walkTopDown()
+ .filter { file -> file.name.endsWith(".apk") }
+ .toList()
+ }
+ .distinct()
+ private val pullParser: XmlPullParser = run {
+ val factory = XmlPullParserFactory.newInstance()
+ factory.isNamespaceAware = true
+ factory.newPullParser()
+ }
+ private val ns = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
+ }
+
+ @Test
+ fun parseExistingApks_NoParseFailures() {
+ val failedParsePackages = mutableListOf<File>()
+ for (apk in apks) {
+ try {
+ parser2.parsePackage(apk, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false)
+ } catch (e: Exception) {
+ if (e.message!!.startsWith("Failed to parse")) {
+ failedParsePackages.add(apk)
+ } else if (e.message!!.startsWith("Skipping target and overlay pair")) {
+ // ignore
+ } else {
+ throw e
+ }
+ }
+ }
+ assertThat(failedParsePackages).isEmpty()
+ }
+
+ @Test
+ fun parseBadManifests() {
+ val tag = "manifest"
+ val prefix = "<manifest $ns>"
+ val suffix = "</manifest>"
+ parseTagBadAttr(tag, "package", 256, )
+ parseTagBadAttr(tag, "android:sharedUserId", 256)
+ parseTagBadAttr(tag, "android:versionName", 4000)
+ parseBadApplicationTags(100, prefix, suffix, tag)
+ parseBadOverlayTags(100, prefix, suffix, tag)
+ parseBadInstrumentationTags(100, prefix, suffix, tag)
+ parseBadPermissionGroupTags(100, prefix, suffix, tag)
+ parseBadPermissionTreeTags(100, prefix, suffix, tag)
+ parseBadSupportsGlTextureTags(100, prefix, suffix, tag)
+ parseBadSupportsScreensTags(100, prefix, suffix, tag)
+ parseBadUsesConfigurationTags(100, prefix, suffix, tag)
+ parseBadUsesPermissionSdk23Tags(100, prefix, suffix, tag)
+ parseBadUsesSdkTags(100, prefix, suffix, tag)
+ parseBadCompatibleScreensTags(200, prefix, suffix, tag)
+ parseBadQueriesTags(200, prefix, suffix, tag)
+ parseBadAttributionTags(400, prefix, suffix, tag)
+ parseBadUsesFeatureTags(400, prefix, suffix, tag)
+ parseBadPermissionTags(2000, prefix, suffix, tag)
+ parseBadUsesPermissionTags(20000, prefix, suffix, tag)
+ }
+
+ private fun parseBadApplicationTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "application"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+
+ parseTagBadAttr(tag, "android:backupAgent", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:manageSpaceActivity", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:process", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:requiredAccountType", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:restrictedAccountType", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:taskAffinity", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+
+ parseBadProfileableTags(100, newPrefix, newSuffix, tag)
+ parseBadUsesNativeLibraryTags(100, newPrefix, newSuffix, tag)
+ parseBadReceiverTags(1000, newPrefix, newSuffix, tag)
+ parseBadServiceTags(1000, newPrefix, newSuffix, tag)
+ parseBadActivityAliasTags(4000, newPrefix, newSuffix, tag)
+ parseBadUsesLibraryTags(4000, newPrefix, newSuffix, tag)
+ parseBadProviderTags(8000, newPrefix, newSuffix, tag)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadActivityTags(40000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadProfileableTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "profileable"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesNativeLibraryTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-native-library"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadReceiverTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "receiver"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:process", 1024, prefix, suffix)
+
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadIntentFilterTags(20000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadServiceTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "service"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:process", 1024, prefix, suffix)
+
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadIntentFilterTags(20000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadActivityAliasTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "activity-alias"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:targetActivity", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadIntentFilterTags(20000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadUsesLibraryTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-library"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadActivityTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "activity"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:parentActivityName", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:process", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:taskAffinity", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadLayoutTags(1000, newPrefix, newSuffix, tag)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadIntentFilterTags(20000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadLayoutTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "layout"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadOverlayTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "overlay"
+ parseTagBadAttr(tag, "android:category", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:requiredSystemPropertyName", 32768, prefix, suffix)
+ parseTagBadAttr(tag, "android:requiredSystemPropertyValue", 256, prefix, suffix)
+ parseTagBadAttr(tag, "android:targetPackage", 256, prefix, suffix)
+ parseTagBadAttr(tag, "android:targetName", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadInstrumentationTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "instrumentation"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:targetPackage", 256, prefix, suffix)
+ parseTagBadAttr(tag, "android:targetProcesses", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadPermissionGroupTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "permission-group"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadPermissionTreeTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "permission-tree"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadSupportsGlTextureTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "supports-gl-texture"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadSupportsScreensTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "supports-screens"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesConfigurationTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-configuration"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesPermissionSdk23Tags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-permission-sdk-23"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesSdkTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-sdk"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadCompatibleScreensTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "compatible-screens"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadScreenTags(4000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadScreenTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "screen"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadQueriesTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "queries"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadPackageTags(1000, newPrefix, newSuffix, tag)
+ parseBadIntentTags(2000, newPrefix, newSuffix, tag)
+ parseBadProviderTags(8000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadPackageTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "package"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadIntentTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "intent"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadActionTags(20000, newPrefix, newSuffix, tag)
+ parseBadCategoryTags(40000, newPrefix, newSuffix, tag)
+ parseBadDataTags(40000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadProviderTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "provider"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:process", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:readPermission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:writePermission", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadGrantUriPermissionTags(100, newPrefix, newSuffix, tag)
+ parseBadPathPermissionTags(100, newPrefix, newSuffix, tag)
+ parseBadMetaDataTags(8000, newPrefix, newSuffix, tag)
+ parseBadIntentFilterTags(20000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadGrantUriPermissionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "grant-uri-permission"
+ parseTagBadAttr(tag, "android:path", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPrefix", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPattern", 4000, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadPathPermissionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "path-permission"
+ parseTagBadAttr(tag, "android:path", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPrefix", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPattern", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:permission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:readPermission", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:writePermission", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadMetaDataTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "meta-data"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:value", 32768, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadIntentFilterTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "intent-filter"
+ val newPrefix = "$prefix<$tag>"
+ val newSuffix = "</$tag>$suffix"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ parseBadActionTags(20000, newPrefix, newSuffix, tag)
+ parseBadCategoryTags(40000, newPrefix, newSuffix, tag)
+ parseBadDataTags(40000, newPrefix, newSuffix, tag)
+ }
+
+ private fun parseBadActionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "action"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadCategoryTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "category"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadDataTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "data"
+ parseTagBadAttr(tag, "android:scheme", 256, prefix, suffix)
+ parseTagBadAttr(tag, "android:host", 256, prefix, suffix)
+ parseTagBadAttr(tag, "android:path", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPattern", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathPrefix", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathSuffix", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:pathAdvancedPattern", 4000, prefix, suffix)
+ parseTagBadAttr(tag, "android:mimeType", 512, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadAttributionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "attribution"
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesFeatureTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-feature"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadPermissionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "permission"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseTagBadAttr(tag, "android:permissionGroup", 256, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseBadUsesPermissionTags(
+ maxNum: Int,
+ prefix: String,
+ suffix: String,
+ parentTag: String
+ ) {
+ val tag = "uses-permission"
+ parseTagBadAttr(tag, "android:name", 1024, prefix, suffix)
+ parseBadTagCount(tag, maxNum, parentTag, prefix, suffix)
+ }
+
+ private fun parseTagBadAttr(
+ tag: String,
+ attrName: String,
+ maxLength: Int,
+ prefix: String = "",
+ suffix: String = ""
+ ) {
+ var attrValue = "x".repeat(maxLength)
+ var tagValue = if (tag.equals("manifest")) "$tag $ns" else tag
+ var manifestStr = "$prefix<$tagValue $attrName=\"$attrValue\" />$suffix"
+ try {
+ parseManifestStr(manifestStr)
+ } catch (e: XmlPullParserException) {
+ fail("Failed to parse valid <$tag> attribute $attrName with max length of $maxLength:" +
+ " ${e.message}")
+ }
+ attrValue = "x".repeat(maxLength + 1)
+ manifestStr = "$prefix<$tagValue $attrName=\"$attrValue\" />$suffix"
+ val e = assertThrows(XmlPullParserException::class.java) {
+ parseManifestStr(manifestStr)
+ }
+ assertEquals(expectedAttrLengthErrorMsg(attrName.split(":").last(), tag), e.message)
+ }
+
+ private fun parseBadTagCount(
+ tag: String,
+ maxNum: Int,
+ parentTag: String,
+ prefix: String,
+ suffix: String
+ ) {
+ var tags = "<$tag />".repeat(maxNum)
+ var manifestStr = "$prefix$tags$suffix"
+ try {
+ parseManifestStr(manifestStr)
+ } catch (e: XmlPullParserException) {
+ fail("Failed to parse <$tag> with max count limit of $maxNum under" +
+ " <$parentTag>: ${e.message}")
+ }
+ tags = "<$tag />".repeat(maxNum + 1)
+ manifestStr = "$prefix$tags$suffix"
+ val e = assertThrows(XmlPullParserException::class.java) {
+ parseManifestStr(manifestStr)
+ }
+ assertEquals(expectedCountErrorMsg(tag, parentTag), e.message)
+ }
+
+ @Test
+ fun parseUnexpectedTag_shouldSkip() {
+ val host = "x".repeat(256)
+ val dataTags = "<data android:host=\"$host\" />".repeat(2049)
+ val ns = "http://schemas.android.com/apk/res/android"
+ val manifestStr = "<manifest xmlns:android=\"$ns\" package=\"test\">$dataTags</manifest>"
+ parseManifestStr(manifestStr)
+ }
+
+ fun parseManifestStr(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
+ fun expectedCountErrorMsg(tag: String, parentTag: String) =
+ "The number of child $tag elements exceeded the max allowed in $parentTag"
+
+ fun expectedAttrLengthErrorMsg(attr: String, tag: String) =
+ "String length limit exceeded for attribute $attr in $tag"
+
+ fun expectedResAttrLengthErrorMsg(tag: String) =
+ "String length limit exceeded for attribute in $tag"
+
+ @Test
+ fun validateResAttrs() {
+ pullParser.setInput(ByteArrayInputStream("<manifest />".toByteArray()), null)
+ pullParser.next()
+ val validator = Validator()
+ validator.validate(pullParser)
+ validateResAttr(pullParser, validator, R.styleable.AndroidManifestData_host,
+ "R.styleable.AndroidManifestData_host", 255)
+ validateResAttr(pullParser, validator, R.styleable.AndroidManifestData_port,
+ "R.styleable.AndroidManifestData_port", 255)
+ validateResAttr(pullParser, validator, R.styleable.AndroidManifestData_scheme,
+ "R.styleable.AndroidManifestData_scheme", 255)
+ validateResAttr(pullParser, validator, R.styleable.AndroidManifestData_mimeType,
+ "R.styleable.AndroidManifestData_mimeType", 512)
+ }
+
+ fun validateResAttr(
+ parser: XmlPullParser,
+ validator: Validator,
+ resId: Int,
+ resIdStr: String,
+ maxLength: Int
+ ) {
+ try {
+ validator.validateAttr(parser, resId, "x".repeat(maxLength))
+ } catch (e: XmlPullParserException) {
+ fail("Failed to parse valid string resource attribute $resIdStr with max length of" +
+ " $maxLength: ${e.message}")
+ }
+ val e = assertThrows(XmlPullParserException::class.java) {
+ validator.validateAttr(parser, resId, "x".repeat(maxLength + 1))
+ }
+ assertEquals(expectedResAttrLengthErrorMsg("manifest"), e.message)
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
index a73fcb89da27..7af4b3d87a3a 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -18,11 +18,18 @@ package com.android.server.power;
import static org.junit.Assert.assertEquals;
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 android.content.Context;
+import android.content.pm.PackageManager;
import android.os.PowerManager;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -34,12 +41,27 @@ import java.util.TimeZone;
*/
public class WakeLockLogTest {
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+ when(mPackageManager.getPackagesForUid(101)).thenReturn(new String[]{ "some.package1" });
+ when(mPackageManager.getPackagesForUid(102)).thenReturn(new String[]{ "some.package2" });
+ }
+
@Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
final int logSize = 20;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
@@ -50,8 +72,10 @@ public class WakeLockLogTest {
PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,on-after-release)\n"
- + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
+ + "(full,acq-causes-wake)\n"
+ " -\n"
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 6\n",
@@ -63,7 +87,7 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 128;
final int logSize = 20;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -72,8 +96,8 @@ public class WakeLockLogTest {
log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial)\n"
- + " 01-01 00:00:01.350 - 102 - ACQ TagFull (full)\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial (partial)\n"
+ + " 01-01 00:00:01.350 - 102 (some.package2) - ACQ TagFull (full)\n"
+ " -\n"
+ " Events: 2, Time-Resets: 1\n"
+ " Buffer, Bytes used: 15\n",
@@ -85,7 +109,7 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 2;
final int logSize = 20;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -95,7 +119,7 @@ public class WakeLockLogTest {
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - --- - ACQ UNKNOWN (partial)\n"
- + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
+ + " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull (full)\n"
+ " -\n"
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 6\n",
@@ -107,26 +131,55 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- // This first item will get deleted when ring buffer loops around
+ // Wake lock 1 acquired - log size = 3
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ // Wake lock 2 acquired - log size = 3 + 3 = 6
when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+
+ // Wake lock 3 acquired - log size = 6 + 3 = 9
when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
log.onWakeLockAcquired("TagThree", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ // We need more space - wake lock 1 acquisition is removed from the log and saved in the
+ // list. Log size = 9 - 3 + 2 = 8
when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
+ log.onWakeLockReleased("TagThree", 101);
+
+ // We need more space - wake lock 2 acquisition is removed from the log and saved in the
+ // list. Log size = 8 - 3 + 2 = 7
+ when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
+ log.onWakeLockReleased("TagPartial", 101);
+
+ // We need more space - wake lock 3 acquisition is removed from the log and saved in the
+ // list. Log size = 7 - 3 + 3 = 7
+ when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
log.onWakeLockAcquired("TagFour", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ // We need more space - wake lock 3 release is removed from the log and wake lock 3
+ // acquisition is removed from the list. Log size = 7 - 2 + 3 = 8
+ when(injectorSpy.currentTimeMillis()).thenReturn(1155L);
+ log.onWakeLockAcquired("TagFive", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ // We need more space - wake lock 1 release is removed from the log and wake lock 1
+ // acquisition is removed from the list. Log size = 8 - 2 + 2 = 8
+ when(injectorSpy.currentTimeMillis()).thenReturn(1156L);
+ log.onWakeLockReleased("TagFull", 102);
+
+ // Wake lock 2 acquisition is still printed because its release have not rolled off the log
+ // yet.
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
- + " 01-01 00:00:01.151 - 101 - ACQ TagThree (partial)\n"
- + " 01-01 00:00:01.152 - 101 - ACQ TagFour (partial)\n"
+ + " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull (full)\n"
+ + " 01-01 00:00:01.154 - 101 (some.package1) - ACQ TagFour (partial)\n"
+ + " 01-01 00:00:01.155 - 101 (some.package1) - ACQ TagFive (partial)\n"
+ + " 01-01 00:00:01.156 - 102 (some.package2) - REL TagFull\n"
+ " -\n"
- + " Events: 3, Time-Resets: 0\n"
- + " Buffer, Bytes used: 9\n",
+ + " Events: 4, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 8\n",
dumpLog(log, false));
}
@@ -135,7 +188,7 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Bad tag means it wont get written
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
@@ -153,14 +206,15 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
PowerManager.PARTIAL_WAKE_LOCK);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.000 - 101 - ACQ *job*/c.o.t.3/.o..Last (partial)\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ "
+ + "*job*/c.o.t.3/.o..Last (partial)\n"
+ " -\n"
+ " Events: 1, Time-Resets: 0\n"
+ " Buffer, Bytes used: 3\n",
@@ -172,7 +226,7 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -180,8 +234,8 @@ public class WakeLockLogTest {
log.onWakeLockReleased("HowdyTag", 101);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.000 - 101 - ACQ HowdyTag (partial)\n"
- + " 01-01 00:00:01.001 - 101 - REL HowdyTag\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
+ + " 01-01 00:00:01.001 - 101 (some.package1) - REL HowdyTag\n"
+ " -\n"
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 5\n"
@@ -194,7 +248,7 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1100L);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
@@ -204,7 +258,7 @@ public class WakeLockLogTest {
log.onWakeLockReleased("HowdyTag", 101);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.100 - 101 - ACQ HowdyTag (partial)\n"
+ + " 01-01 00:00:01.100 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
+ " -\n"
+ " Events: 1, Time-Resets: 0\n"
+ " Buffer, Bytes used: 3\n",
@@ -216,20 +270,153 @@ public class WakeLockLogTest {
final int tagDatabaseSize = 6;
final int logSize = 10;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
- WakeLockLog log = new WakeLockLog(injectorSpy);
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK);
assertEquals("Wake Lock Log\n"
- + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,system-wakelock)\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ + "(partial,system-wakelock)\n"
+ " -\n"
+ " Events: 1, Time-Resets: 0\n"
+ " Buffer, Bytes used: 3\n",
dumpLog(log, false));
}
+ @Test
+ public void testAddItemWithNoPackageName() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+ when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " -\n"
+ + " Events: 1, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 3\n",
+ dumpLog(log, false));
+ }
+
+ @Test
+ public void testAddItemWithMultiplePackageNames() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+ when(mPackageManager.getPackagesForUid(101)).thenReturn(
+ new String[]{ "some.package1", "some.package2", "some.package3" });
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1,...) - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " -\n"
+ + " Events: 1, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 3\n",
+ dumpLog(log, false));
+ }
+
+ @Test
+ public void testAddItemsWithRepeatOwnerUid_UsesCache() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
+ log.onWakeLockAcquired("TagFull2", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " 01-01 00:00:01.150 - 101 (some.package1) - ACQ TagFull "
+ + "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.151 - 101 (some.package1) - ACQ TagFull2 "
+ + "(full,acq-causes-wake)\n"
+ + " -\n"
+ + " Events: 3, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 9\n",
+ dumpLog(log, false));
+
+ verify(mPackageManager, times(1)).getPackagesForUid(101);
+ }
+
+ @Test
+ public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
+ log.onWakeLockAcquired("TagFull2", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
+ log.onWakeLockAcquired("TagFull3", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
+ log.onWakeLockAcquired("TagFull4", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
+ log.onWakeLockAcquired("TagFull5", 101,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ // The first 3 events have been removed from the log and they exist in the saved
+ // acquisitions list. They should also use the cache when fetching the package names.
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " 01-01 00:00:01.150 - 101 (some.package1) - ACQ TagFull "
+ + "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.151 - 101 (some.package1) - ACQ TagFull2 "
+ + "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.152 - 101 (some.package1) - ACQ TagFull3 "
+ + "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.153 - 101 (some.package1) - ACQ TagFull4 "
+ + "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.154 - 101 (some.package1) - ACQ TagFull5 "
+ + "(full,acq-causes-wake)\n"
+ + " -\n"
+ + " Events: 6, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 9\n",
+ dumpLog(log, false));
+
+ verify(mPackageManager, times(1)).getPackagesForUid(101);
+ }
+
private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index cff4cc72de52..5a2d2e3d33fd 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -42,6 +42,7 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import org.junit.Before;
@@ -214,6 +215,15 @@ public class BatteryExternalStatsWorkerTest {
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
mPowerProfile = new PowerProfile(context, true /* forTest */);
+
+ SparseArray<int[]> cpusByPolicy = new SparseArray<>();
+ cpusByPolicy.put(0, new int[]{0, 1, 2, 3});
+ cpusByPolicy.put(4, new int[]{4, 5, 6, 7});
+ SparseArray<int[]> freqsByPolicy = new SparseArray<>();
+ freqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
+ freqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ mCpuScalingPolicies = new CpuScalingPolicies(freqsByPolicy, freqsByPolicy);
+
initTimersAndCounters();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index f48a296358c4..55ffa1a15a6b 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -32,25 +32,25 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.BatteryStats;
import android.os.UserHandle;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.Display;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.os.PowerProfile;
import com.android.internal.util.ArrayUtils;
import org.junit.Before;
@@ -95,8 +95,6 @@ public class BatteryStatsCpuTimesTest {
SystemServerCpuThreadReader mSystemServerCpuThreadReader;
@Mock
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
- @Mock
- PowerProfile mPowerProfile;
private MockClock mClocks;
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -108,6 +106,7 @@ public class BatteryStatsCpuTimesTest {
mClocks = new MockClock();
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
+ .setTestCpuScalingPolicies()
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
.setKernelCpuUidActiveTimeReader(mCpuUidActiveTimeReader)
@@ -119,18 +118,14 @@ public class BatteryStatsCpuTimesTest {
@Test
public void testUpdateCpuTimeLocked() {
// PRECONDITIONS
- mBatteryStatsImpl.setPowerProfile(mPowerProfile);
mBatteryStatsImpl.setOnBatteryInternal(false);
final int numClusters = 3;
initKernelCpuSpeedReaders(numClusters);
- final long[] freqs = {1, 12, 123, 12, 1234};
- when(mCpuUidFreqTimeReader.readFreqs(mPowerProfile)).thenReturn(freqs);
// RUN
mBatteryStatsImpl.updateCpuTimeLocked(false, false, null);
// VERIFY
- assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
verify(mCpuUidUserSysTimeReader).readDelta(anyBoolean(), isNull());
verify(mCpuUidFreqTimeReader).readDelta(anyBoolean(), isNull());
for (int i = 0; i < numClusters; ++i) {
@@ -153,9 +148,8 @@ public class BatteryStatsCpuTimesTest {
verify(mUserInfoProvider).refreshUserIds();
verify(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
- // perClusterTimesAvailable is called twice, once in updateCpuTimeLocked() and the other
- // in readKernelUidCpuFreqTimesLocked.
- verify(mCpuUidFreqTimeReader, times(2)).perClusterTimesAvailable();
+ verify(mCpuUidFreqTimeReader).perClusterTimesAvailable();
+ verify(mCpuUidFreqTimeReader).allUidTimesAvailable();
verify(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
verify(mCpuUidActiveTimeReader).readAbsolute(
@@ -210,15 +204,25 @@ public class BatteryStatsCpuTimesTest {
// PRECONDITIONS
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
+
+ CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ for (int i = 0; i < clusterSpeedTimesMs.length; i++) {
+ put(i, new int[]{i});
+ }
+ }},
+ new SparseArray<>() {{
+ for (int i = 0; i < clusterSpeedTimesMs.length; i++) {
+ put(i, new int[clusterSpeedTimesMs[i].length]);
+ }
+ }});
+ mBatteryStatsImpl.setCpuScalingPolicies(scalingPolicies);
+
initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
when(mKernelCpuSpeedReaders[i].readDelta()).thenReturn(clusterSpeedTimesMs[i]);
}
- when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterSpeedTimesMs.length);
- for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
- when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
- .thenReturn(clusterSpeedTimesMs[i].length);
- }
+
final SparseLongArray updatedUids = new SparseLongArray();
final int[] testUids = {10012, 10014, 10016};
final int[] cpuTimeUs = {89, 31, 43};
@@ -626,14 +630,19 @@ public class BatteryStatsCpuTimesTest {
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
- final long[] freqs = {1, 12, 123, 12, 1234};
+ CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[]{0});
+ put(1, new int[]{1});
+ }},
+ new SparseArray<>() {{
+ put(0, new int[]{1, 12, 123});
+ put(1, new int[]{12, 1234});
+ }});
+ mBatteryStatsImpl.setCpuScalingPolicies(scalingPolicies);
// Derived from freqs above, 2 clusters with {3, 2} freqs in each of them.
final int[] clusterFreqs = {3, 2};
- when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterFreqs.length);
- for (int i = 0; i < clusterFreqs.length; ++i) {
- when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
- .thenReturn(clusterFreqs[i]);
- }
+
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
@@ -736,14 +745,21 @@ public class BatteryStatsCpuTimesTest {
};
final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers
= getPartialTimers(partialTimerUids);
- final long[] freqs = {1, 12, 123, 12, 1234};
+
+ CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[]{0});
+ put(1, new int[]{1});
+ }},
+ new SparseArray<>() {{
+ put(0, new int[]{1, 12, 123});
+ put(1, new int[]{12, 1234});
+ }});
+ mBatteryStatsImpl.setCpuScalingPolicies(scalingPolicies);
+
// Derived from freqs above, 2 clusters with {3, 2} freqs in each of them.
final int[] clusterFreqs = {3, 2};
- when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterFreqs.length);
- for (int i = 0; i < clusterFreqs.length; ++i) {
- when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
- .thenReturn(clusterFreqs[i]);
- }
+
final long[][] uidTimesMs = {
{4, 10, 5, 9, 4},
{5, 1, 12, 2, 10},
@@ -764,7 +780,7 @@ public class BatteryStatsCpuTimesTest {
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(partialTimers, true, false, null);
// VERIFY
- final long[][] expectedWakeLockUidTimesUs = new long[clusterFreqs.length][];
+ final long[][] expectedWakeLockUidTimesUs = new long[2][];
for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
expectedWakeLockUidTimesUs[cluster] = new long[clusterFreqs[cluster]];
}
@@ -1357,11 +1373,7 @@ public class BatteryStatsCpuTimesTest {
private void updateTimeBasesLocked(boolean unplugged, int screenState,
long upTime, long realTime) {
- // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
- // BatteryStatsImpl.updateCpuTimeLocked
- mBatteryStatsImpl.setPowerProfile(null);
mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenState, upTime, realTime);
- mBatteryStatsImpl.setPowerProfile(mPowerProfile);
}
private void initKernelCpuSpeedReaders(int count) {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index b2dad7348525..f20f061230e2 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -50,6 +50,7 @@ import android.view.Display;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelSingleUidTimeReader;
import com.android.internal.os.LongArrayMultiStateCounter;
@@ -70,9 +71,6 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
public class BatteryStatsImplTest {
- private static final long[] CPU_FREQS = {1, 2, 3, 4, 5};
- private static final int NUM_CPU_FREQS = CPU_FREQS.length;
-
@Mock
private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
@Mock
@@ -83,6 +81,16 @@ public class BatteryStatsImplTest {
private KernelWakelockReader mKernelWakelockReader;
private KernelWakelockStats mKernelWakelockStats = new KernelWakelockStats();
+ private static final int NUM_CPU_FREQS = 5;
+
+ private final CpuScalingPolicies mCpuScalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[1]);
+ }},
+ new SparseArray<>() {{
+ put(0, new int[NUM_CPU_FREQS]);
+ }});
+
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -91,13 +99,13 @@ public class BatteryStatsImplTest {
MockitoAnnotations.initMocks(this);
when(mKernelUidCpuFreqTimeReader.isFastCpuTimesReader()).thenReturn(true);
- when(mKernelUidCpuFreqTimeReader.readFreqs(any())).thenReturn(CPU_FREQS);
when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
when(mKernelWakelockReader.readKernelWakelockStats(
any(KernelWakelockStats.class))).thenReturn(mKernelWakelockStats);
mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
.setPowerProfile(mPowerProfile)
+ .setCpuScalingPolicies(mCpuScalingPolicies)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
.setKernelWakelockReader(mKernelWakelockReader);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index e6454e4ce040..090c8c8c2c3c 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -1457,6 +1457,148 @@ public class BatteryStatsNoteTest extends TestCase {
}
@SmallTest
+ public void testGetPerStateActiveRadioDurationMs_initialModemActivity() {
+ final MockClock clock = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
+ bi.setPowerProfile(mock(PowerProfile.class));
+
+ final int ratCount = RADIO_ACCESS_TECHNOLOGY_COUNT;
+ final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1;
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+
+ List<ActivityStatsTechSpecificInfo> specificInfoList = new ArrayList();
+
+ final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ final long[][] expectedRxDurationsMs = new long[ratCount][frequencyCount];
+ final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedRxDurationsMs[rat][freq] = 0;
+ } else {
+ expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE;
+ }
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedTxDurationsMs[rat][freq][txLvl] = 0;
+ } else {
+ expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE;
+ }
+ }
+ }
+ }
+
+ // The first modem activity pulled from modem with activity stats for each RATs.
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 101));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 202));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 303));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[]{3, 9, 133, 48, 218}, 404));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 505));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.IWLAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 606));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 707));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_LOW, new int[txLevelCount], 808));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MID, new int[txLevelCount], 909));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_HIGH, new int[txLevelCount], 1010));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE, new int[txLevelCount], 1111));
+
+
+ final ActivityStatsTechSpecificInfo[] specificInfos = specificInfoList.toArray(
+ new ActivityStatsTechSpecificInfo[specificInfoList.size()]);
+ final ModemActivityInfo mai = new ModemActivityInfo(0L, 2002L, 3003L, specificInfos);
+ final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai, specificInfos);
+
+
+ IntConsumer incrementTime = inc -> {
+ state.currentTimeMs += inc;
+ clock.realtime = clock.uptime = state.currentTimeMs;
+
+ final int currRat = state.currentRat;
+ final int currRant = state.currentRadioAccessNetworkType;
+ final int currFreqRange =
+ currRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0;
+ int currSignalStrength = state.currentSignalStrengths.get(currRat);
+
+ if (state.modemActive) {
+ // Don't count the duration if the modem is not active
+ expectedDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+ }
+
+ // Evaluate the HAL provided time in states.
+ final ActivityStatsTechSpecificInfo info = state.getSpecificInfo(currRant,
+ currFreqRange);
+ switch (state.modemState) {
+ case SLEEP:
+ long sleepMs = state.modemActivityInfo.getSleepTimeMillis();
+ state.modemActivityInfo.setSleepTimeMillis(sleepMs + inc);
+ break;
+ case IDLE:
+ long idleMs = state.modemActivityInfo.getIdleTimeMillis();
+ state.modemActivityInfo.setIdleTimeMillis(idleMs + inc);
+ break;
+ case RECEIVING:
+ long rxMs = info.getReceiveTimeMillis();
+ info.setReceiveTimeMillis(rxMs + inc);
+ expectedRxDurationsMs[currRat][currFreqRange] += inc;
+ break;
+ case TRANSMITTING:
+ int[] txMs = info.getTransmitTimeMillis().clone();
+ txMs[currSignalStrength] += inc;
+ info.setTransmitTimeMillis(txMs);
+ expectedTxDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+ break;
+ }
+ };
+
+ // On battery, but the modem is not active
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, state.currentTimeMs * 1000,
+ state.currentTimeMs * 1000);
+ bi.setOnBatteryInternal(true);
+ state.noteModemControllerActivity();
+ // Ensure the first modem activity should not be counted up.
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ // Start counting.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ AccessNetworkConstants.AccessNetworkType.NGRAN);
+ // Frequency changed to low.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW);
+ incrementTime.accept(300);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(500);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(600);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ }
+
+ @SmallTest
public void testGetPerStateActiveRadioDurationMs_withModemActivity() {
final MockClock clock = new MockClock(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 3135215d65f7..534aa89e1699 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -34,6 +34,7 @@ import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.power.EnergyConsumerStats;
@@ -59,6 +60,9 @@ public class BatteryUsageStatsRule implements TestRule {
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
+ private boolean mDefaultCpuScalingPolicy = true;
+ private SparseArray<int[]> mCpusByPolicy = new SparseArray<>();
+ private SparseArray<int[]> mFreqsByPolicy = new SparseArray<>();
public BatteryUsageStatsRule() {
this(0, null);
@@ -74,6 +78,13 @@ public class BatteryUsageStatsRule implements TestRule {
mMockClock.currentTime = currentTime;
mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
mBatteryStats.setPowerProfile(mPowerProfile);
+
+ mCpusByPolicy.put(0, new int[]{0, 1, 2, 3});
+ mCpusByPolicy.put(4, new int[]{4, 5, 6, 7});
+ mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
+ mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
+
mBatteryStats.onSystemReady();
}
@@ -82,6 +93,19 @@ public class BatteryUsageStatsRule implements TestRule {
return this;
}
+ public BatteryUsageStatsRule setCpuScalingPolicy(int policy, int[] relatedCpus,
+ int[] frequencies) {
+ if (mDefaultCpuScalingPolicy) {
+ mCpusByPolicy.clear();
+ mFreqsByPolicy.clear();
+ mDefaultCpuScalingPolicy = false;
+ }
+ mCpusByPolicy.put(policy, relatedCpus);
+ mFreqsByPolicy.put(policy, frequencies);
+ mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
+ return this;
+ }
+
public BatteryUsageStatsRule setAveragePower(String key, double value) {
when(mPowerProfile.getAveragePower(key)).thenReturn(value);
when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())).thenReturn(value);
@@ -103,23 +127,14 @@ public class BatteryUsageStatsRule implements TestRule {
return this;
}
- public BatteryUsageStatsRule setNumCpuClusters(int number) {
- when(mPowerProfile.getNumCpuClusters()).thenReturn(number);
- return this;
- }
-
- public BatteryUsageStatsRule setNumSpeedStepsInCpuCluster(int cluster, int speeds) {
- when(mPowerProfile.getNumSpeedStepsInCpuCluster(cluster)).thenReturn(speeds);
+ public BatteryUsageStatsRule setAveragePowerForCpuScalingPolicy(int policy, double value) {
+ when(mPowerProfile.getAveragePowerForCpuScalingPolicy(policy)).thenReturn(value);
return this;
}
- public BatteryUsageStatsRule setAveragePowerForCpuCluster(int cluster, double value) {
- when(mPowerProfile.getAveragePowerForCpuCluster(cluster)).thenReturn(value);
- return this;
- }
-
- public BatteryUsageStatsRule setAveragePowerForCpuCore(int cluster, int step, double value) {
- when(mPowerProfile.getAveragePowerForCpuCore(cluster, step)).thenReturn(value);
+ public BatteryUsageStatsRule setAveragePowerForCpuScalingStep(int policy, int step,
+ double value) {
+ when(mPowerProfile.getAveragePowerForCpuScalingStep(policy, step)).thenReturn(value);
return this;
}
@@ -193,6 +208,12 @@ public class BatteryUsageStatsRule implements TestRule {
return mPowerProfile;
}
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ synchronized (mBatteryStats) {
+ return mBatteryStats.getCpuScalingPolicies();
+ }
+ }
+
public MockBatteryStatsImpl getBatteryStats() {
return mBatteryStats;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index ced996f35bdf..888bc623f669 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -65,15 +65,14 @@ public class CpuPowerCalculatorTest {
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
- .setNumCpuClusters(2)
- .setNumSpeedStepsInCpuCluster(0, 2)
- .setNumSpeedStepsInCpuCluster(1, 2)
- .setAveragePowerForCpuCluster(0, 360)
- .setAveragePowerForCpuCluster(1, 480)
- .setAveragePowerForCpuCore(0, 0, 300)
- .setAveragePowerForCpuCore(0, 1, 400)
- .setAveragePowerForCpuCore(1, 0, 500)
- .setAveragePowerForCpuCore(1, 1, 600);
+ .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
+ .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300, 400})
+ .setAveragePowerForCpuScalingPolicy(0, 360)
+ .setAveragePowerForCpuScalingPolicy(2, 480)
+ .setAveragePowerForCpuScalingStep(0, 0, 300)
+ .setAveragePowerForCpuScalingStep(0, 1, 400)
+ .setAveragePowerForCpuScalingStep(2, 0, 500)
+ .setAveragePowerForCpuScalingStep(2, 1, 600);
private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
mock(KernelCpuSpeedReader.class),
@@ -179,7 +178,8 @@ public class CpuPowerCalculatorTest {
mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
CpuPowerCalculator calculator =
- new CpuPowerCalculator(mStatsRule.getPowerProfile());
+ new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -246,8 +246,8 @@ public class CpuPowerCalculatorTest {
mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
- CpuPowerCalculator calculator =
- new CpuPowerCalculator(mStatsRule.getPowerProfile());
+ CpuPowerCalculator calculator = new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
@@ -287,7 +287,6 @@ public class CpuPowerCalculatorTest {
when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
when(mMockCpuUidFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
- when(mMockCpuUidFreqTimeReader.readFreqs(any())).thenReturn(new long[]{100, 200, 300, 400});
when(mMockKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
@@ -353,8 +352,8 @@ public class CpuPowerCalculatorTest {
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
mStatsRule.getBatteryStats().updateCpuTimesForAllUids();
- CpuPowerCalculator calculator =
- new CpuPowerCalculator(mStatsRule.getPowerProfile());
+ CpuPowerCalculator calculator = new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile());
mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
.powerProfileModeledOnly()
@@ -461,8 +460,8 @@ public class CpuPowerCalculatorTest {
clusterChargesUC[1] += 20000000;
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);
- CpuPowerCalculator calculator =
- new CpuPowerCalculator(mStatsRule.getPowerProfile());
+ CpuPowerCalculator calculator = new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile());
mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
.includePowerModels()
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 62e56f9eeed1..d3ec0d7e3f6e 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -76,6 +76,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -193,6 +196,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -347,6 +353,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -583,6 +592,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -660,6 +672,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, -1,
0, 0);
@@ -841,6 +856,9 @@ public class MobileRadioPowerCalculatorTest {
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, -1,
0, 0);
@@ -1023,4 +1041,10 @@ public class MobileRadioPowerCalculatorTest {
assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(2.08130);
assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
}
+
+ public void setInitialEmptyModemActivityInfo(BatteryStatsImpl stats) {
+ // Initial empty ModemActivityInfo.
+ final ModemActivityInfo emptyMai = new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L);
+ stats.noteModemControllerActivity(emptyMai, 0, 0, 0, mNetworkStatsManager);
+ }
}
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 b032cbe67485..6d3f1f27b572 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
@@ -16,18 +16,18 @@
package com.android.server.power.stats;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.usage.NetworkStatsManager;
import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Clock;
+import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
@@ -73,7 +73,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
};
mCpuUidFreqTimeReader = mock(KernelCpuUidFreqTimeReader.class);
- when(mCpuUidFreqTimeReader.readFreqs(any())).thenReturn(new long[]{100, 200});
mKernelWakelockReader = null;
}
@@ -136,8 +135,25 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
@NonNull NetworkStatsManager networkStatsManager) {
return mNetworkStats;
}
+
public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
mPowerProfile = powerProfile;
+ setTestCpuScalingPolicies();
+ return this;
+ }
+
+ public MockBatteryStatsImpl setTestCpuScalingPolicies() {
+ SparseArray<int[]> cpusByPolicy = new SparseArray<>();
+ cpusByPolicy.put(0, new int[]{0});
+ SparseArray<int[]> freqsByPolicy = new SparseArray<>();
+ freqsByPolicy.put(0, new int[]{100, 200, 300});
+
+ setCpuScalingPolicies(new CpuScalingPolicies(freqsByPolicy, freqsByPolicy));
+ return this;
+ }
+
+ public MockBatteryStatsImpl setCpuScalingPolicies(CpuScalingPolicies cpuScalingPolicies) {
+ mCpuScalingPolicies = cpuScalingPolicies;
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index 3c5c0a39745b..4dae2d548057 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -62,15 +62,14 @@ public class SystemServicePowerCalculatorTest {
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
- .setNumCpuClusters(2)
- .setNumSpeedStepsInCpuCluster(0, 2)
- .setNumSpeedStepsInCpuCluster(1, 2)
- .setAveragePowerForCpuCluster(0, 360)
- .setAveragePowerForCpuCluster(1, 480)
- .setAveragePowerForCpuCore(0, 0, 300)
- .setAveragePowerForCpuCore(0, 1, 400)
- .setAveragePowerForCpuCore(1, 0, 500)
- .setAveragePowerForCpuCore(1, 1, 600);
+ .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
+ .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300, 400})
+ .setAveragePowerForCpuScalingPolicy(0, 360)
+ .setAveragePowerForCpuScalingPolicy(2, 480)
+ .setAveragePowerForCpuScalingStep(0, 0, 300)
+ .setAveragePowerForCpuScalingStep(0, 1, 400)
+ .setAveragePowerForCpuScalingStep(2, 0, 500)
+ .setAveragePowerForCpuScalingStep(2, 1, 600);
@Mock
private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
@@ -113,9 +112,10 @@ public class SystemServicePowerCalculatorTest {
prepareBatteryStats(null);
SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
- mStatsRule.getPowerProfile());
+ mStatsRule.getCpuScalingPolicies(), mStatsRule.getPowerProfile());
- mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
+ mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile()), calculator);
assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
@@ -145,9 +145,10 @@ public class SystemServicePowerCalculatorTest {
prepareBatteryStats(new long[]{50000000, 100000000});
SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
- mStatsRule.getPowerProfile());
+ mStatsRule.getCpuScalingPolicies(), mStatsRule.getPowerProfile());
- mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
+ mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getCpuScalingPolicies(),
+ mStatsRule.getPowerProfile()), calculator);
assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index d1814199a0b1..b81b776019f9 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -106,7 +106,7 @@ public class CpuWakeupStatsTest {
for (int i = 0; i < 100; i++) {
final long now = mRandom.nextLong(retention + 1, 100 * retention);
obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_SOUND_TRIGGER_IRQ);
- assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
+ assertThat(obj.mWakeupEvents.lastIndexOnOrBefore(now - retention)).isLessThan(0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
index b02618e97b11..99bc25abc4a1 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
@@ -18,8 +18,8 @@ package com.android.server.power.stats.wakeups;
import static com.google.common.truth.Truth.assertThat;
+import android.util.LongSparseArray;
import android.util.SparseIntArray;
-import android.util.TimeSparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -73,7 +73,7 @@ public class WakingActivityHistoryTest {
assertThat(history.mWakingActivity.contains(subsystem + 1)).isFalse();
assertThat(history.mWakingActivity.contains(subsystem - 1)).isFalse();
- final TimeSparseArray<SparseIntArray> recordedHistory = history.mWakingActivity.get(
+ final LongSparseArray<SparseIntArray> recordedHistory = history.mWakingActivity.get(
subsystem);
assertThat(recordedHistory.size()).isEqualTo(1);
@@ -119,7 +119,7 @@ public class WakingActivityHistoryTest {
assertThat(history.mWakingActivity.contains(subsystem + 1)).isFalse();
assertThat(history.mWakingActivity.contains(subsystem - 1)).isFalse();
- final TimeSparseArray<SparseIntArray> recordedHistory = history.mWakingActivity.get(
+ final LongSparseArray<SparseIntArray> recordedHistory = history.mWakingActivity.get(
subsystem);
assertThat(recordedHistory.size()).isEqualTo(1);
@@ -266,7 +266,7 @@ public class WakingActivityHistoryTest {
final long time = random.nextLong(firstTime + mTestRetention + 100,
456 * mTestRetention);
history.recordActivity(subsystem, time, new SparseIntArray());
- assertThat(history.mWakingActivity.get(subsystem).closestIndexOnOrBefore(
+ assertThat(history.mWakingActivity.get(subsystem).lastIndexOnOrBefore(
time - mTestRetention)).isLessThan(0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 3adee0f06761..83fa29a5dd66 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -31,7 +31,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
-import android.util.TimeSparseArray;
+import android.util.LongSparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -568,7 +568,7 @@ public class UsageStatsDatabaseTest {
mUsageStatsDatabase.forceIndexFiles();
final int len = mUsageStatsDatabase.mSortedStatFiles.length;
for (int i = 0; i < len; i++) {
- final TimeSparseArray<AtomicFile> files = mUsageStatsDatabase.mSortedStatFiles[i];
+ final LongSparseArray<AtomicFile> files = mUsageStatsDatabase.mSortedStatFiles[i];
// The stats file for each interval type equals to max allowed.
assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i],
files.size());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index eaf483869be4..ba0743980077 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4150,6 +4150,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testSetListenerAccessForUser_grantWithNameTooLong_throws() {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ true, true));
+ }
+
+ @Test
+ public void testSetListenerAccessForUser_revokeWithNameTooLong_okay() throws Exception {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ false, true);
+
+ verify(mListeners).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, /* enabled= */ false, true);
+ }
+
+ @Test
public void testSetAssistantAccessForUser() throws Exception {
UserInfo ui = new UserInfo();
ui.id = mContext.getUserId() + 10;
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
index fc4e6e0a5d66..efe1af336ecd 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLoggingLatencyTest.java
@@ -38,6 +38,7 @@ import android.os.BatteryStatsInternal;
import android.os.Process;
import android.os.RemoteException;
+import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.FakeLatencyTracker;
@@ -47,6 +48,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
@@ -54,10 +56,13 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(JUnit4.class)
+@FlakyTest(bugId = 275746222)
public class SoundTriggerMiddlewareLoggingLatencyTest {
@Rule
public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+ @Rule
+ public Timeout mGlobalTimeout = Timeout.seconds(30);
private FakeLatencyTracker mLatencyTracker;
@Mock
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 5369b93c9534..bdd178b0b317 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2046,6 +2046,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Once transition starts, rotation is applied and transition shows DC rotating.
testPlayer.startTransition();
+ waitUntilHandlersIdle();
assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
assertNotNull(testPlayer.mLastReady);
assertTrue(testPlayer.mController.isPlaying());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 2a8f0ffc4d49..42422d91d598 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -26,6 +26,7 @@ import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -1128,6 +1129,22 @@ public class DisplayRotationTests {
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
+ @Test
+ public void testReturnsUserRotation_FixedToUserRotationIfNoAutoRotation_AutoRotationNotSupport()
+ throws Exception {
+ mBuilder.setSupportAutoRotation(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+ mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
// ========================
// Non-rotation API Tests
// ========================
@@ -1148,6 +1165,15 @@ public class DisplayRotationTests {
+ " fixed to user rotation.", mTarget.isFixedToUserRotation());
}
+ @Test
+ public void testIsFixedToUserRotation_FixedToUserRotationIfNoAutoRotation() throws Exception {
+ mBuilder.build();
+ mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_IF_NO_AUTO_ROTATION);
+
+ assertFalse("Display rotation should respect app requested orientation if"
+ + " fixed to user rotation if no auto rotation.", mTarget.isFixedToUserRotation());
+ }
+
private void moveTimeForward(long timeMillis) {
sCurrentUptimeMillis += timeMillis;
sClock.fastForward(timeMillis);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 204cbf79dba9..994dcf1e2ea5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -31,17 +31,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
@@ -268,8 +263,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
navBar.setHasSurface(true);
navBarProvider.setServerVisible(true);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
// Make both system bars invisible.
mAppWindow.setRequestedVisibleTypes(
@@ -305,8 +298,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
addNavigationBar().getControllableInsetProvider().setServerVisible(true);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
@@ -341,8 +332,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
mAppWindow.mAboveInsetsState.addSource(navBarSource);
mAppWindow.mAboveInsetsState.addSource(statusBarSource);
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
@@ -390,8 +379,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
final WindowState app2 = addWindow(TYPE_APPLICATION, "app");
final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
- spyOn(policy);
- doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
policy.showTransient(navigationBars() | statusBars(),
true /* isGestureOnSystemBar */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 28241d30e168..f332b6988da0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -563,6 +563,36 @@ public class TaskTests extends WindowTestsBase {
assertEquals(freeformBounds, task.getBounds());
}
+ @Test
+ public void testTopActivityEligibleForUserAspectRatioButton() {
+ DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ final Task task = rootTask.getBottomMostTask();
+ final ActivityRecord root = task.getTopNonFinishingActivity();
+ spyOn(mWm.mLetterboxConfiguration);
+
+ // When device config flag is disabled the button is not enabled
+ doReturn(false).when(mWm.mLetterboxConfiguration)
+ .isUserAppAspectRatioSettingsEnabled();
+ doReturn(false).when(mWm.mLetterboxConfiguration)
+ .isTranslucentLetterboxingEnabled();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+
+ // The flag is enabled
+ doReturn(true).when(mWm.mLetterboxConfiguration)
+ .isUserAppAspectRatioSettingsEnabled();
+ spyOn(root);
+ doReturn(task).when(root).getOrganizedTask();
+ // When the flag is enabled and the top activity is not in size compat mode.
+ doReturn(false).when(root).inSizeCompatMode();
+ assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+
+ // When in size compat mode the button is not enabled
+ doReturn(true).when(root).inSizeCompatMode();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ }
+
/**
* Tests that a task with forced orientation has orientation-consistent bounds within the
* parent.
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 4639ee0ae33d..45ecc3f762ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -77,6 +77,10 @@ class TestDisplayContent extends DisplayContent {
spyOn(inputMonitor);
doNothing().when(inputMonitor).resumeDispatchingLw(any());
+ final InsetsPolicy insetsPolicy = getInsetsPolicy();
+ WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
+ WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
+
// For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
// See DisplayRotation#readDefaultDisplayRotation for context.
// Without that, meaning of height and width in context of the tests can be swapped if
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 adf3f3976f38..bd111ada8550 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -233,7 +233,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
}
public void setSafeMode(boolean safeMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 5b7b1b297a1d..5c1b262a9dc8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -82,6 +82,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
+import android.view.ContentRecordingSession;
import android.view.IWindow;
import android.view.IWindowSessionCallback;
import android.view.InputChannel;
@@ -102,6 +103,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
import com.android.internal.os.IResultReceiver;
+import com.android.server.LocalServices;
import org.junit.Rule;
import org.junit.Test;
@@ -769,6 +771,63 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
+ public void setContentRecordingSession_sessionNull_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+
+ boolean result = wmInternal.setContentRecordingSession(/* incomingSession= */ null);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentDisplay_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentTask_noMatchingTask_returnsFalse() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ IBinder launchCookie = new Binder();
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(launchCookie);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ ActivityRecord activityRecord = createActivityRecord(createTask(mDefaultDisplay));
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(
+ activityRecord.mLaunchCookie);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ Task task = createTask(mDefaultDisplay);
+ ActivityRecord activityRecord = createActivityRecord(task);
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(
+ activityRecord.mLaunchCookie);
+
+ wmInternal.setContentRecordingSession(session);
+
+ assertThat(session.getTokenToRecord()).isEqualTo(
+ task.mRemoteToken.toWindowContainerToken().asBinder());
+ }
+
+ @Test
public void testisLetterboxBackgroundMultiColored() {
assertThat(setupLetterboxConfigurationWithBackgroundType(
LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING)).isTrue();
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 be8ee7832a5d..d5547ec69247 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -222,6 +222,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
displayPolicy.finishWindowsDrawn();
displayPolicy.finishScreenTurningOn();
+ final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy();
+ suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
+ suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
+
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);
@@ -278,6 +282,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
checkDeviceSpecificOverridesNotApplied();
}
+ /**
+ * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from
+ * controlling them, or it will cause {@link NullPointerException}.
+ */
+ static void suppressInsetsAnimation(InsetsControlTarget target) {
+ spyOn(target);
+ Mockito.doNothing().when(target).notifyInsetsControlChanged();
+ }
+
@After
public void tearDown() throws Exception {
if (mUseFakeSettingsProvider) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index a8e374f0edc2..f1c5865fff73 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -26,9 +26,9 @@ import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeSparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -117,8 +117,8 @@ public class UsageStatsDatabase {
private final Object mLock = new Object();
private final File[] mIntervalDirs;
- @VisibleForTesting
- final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ final LongSparseArray<AtomicFile>[] mSortedStatFiles;
private final UnixCalendar mCal;
private final File mVersionFile;
private final File mBackupsDir;
@@ -155,7 +155,7 @@ public class UsageStatsDatabase {
mVersionFile = new File(dir, "version");
mBackupsDir = new File(dir, "backups");
mUpdateBreadcrumb = new File(dir, "breadcrumb");
- mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
+ mSortedStatFiles = new LongSparseArray[mIntervalDirs.length];
mPackageMappingsFile = new File(dir, "mappings");
mCal = new UnixCalendar(0);
}
@@ -186,8 +186,8 @@ public class UsageStatsDatabase {
}
// Delete files that are in the future.
- for (TimeSparseArray<AtomicFile> files : mSortedStatFiles) {
- final int startIndex = files.closestIndexOnOrAfter(currentTimeMillis);
+ for (LongSparseArray<AtomicFile> files : mSortedStatFiles) {
+ final int startIndex = files.firstIndexOnOrAfter(currentTimeMillis);
if (startIndex < 0) {
continue;
}
@@ -221,7 +221,7 @@ public class UsageStatsDatabase {
*/
public boolean checkinDailyFiles(CheckinAction checkinAction) {
synchronized (mLock) {
- final TimeSparseArray<AtomicFile> files =
+ final LongSparseArray<AtomicFile> files =
mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY];
final int fileCount = files.size();
@@ -292,7 +292,7 @@ public class UsageStatsDatabase {
// Index the available usage stat files on disk.
for (int i = 0; i < mSortedStatFiles.length; i++) {
if (mSortedStatFiles[i] == null) {
- mSortedStatFiles[i] = new TimeSparseArray<>();
+ mSortedStatFiles[i] = new LongSparseArray<>();
} else {
mSortedStatFiles[i].clear();
}
@@ -700,7 +700,7 @@ public class UsageStatsDatabase {
int filesDeleted = 0;
int filesMoved = 0;
- for (TimeSparseArray<AtomicFile> files : mSortedStatFiles) {
+ for (LongSparseArray<AtomicFile> files : mSortedStatFiles) {
final int fileCount = files.size();
for (int i = 0; i < fileCount; i++) {
final AtomicFile file = files.valueAt(i);
@@ -834,9 +834,9 @@ public class UsageStatsDatabase {
}
synchronized (mLock) {
- final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
+ final LongSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
- int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
+ int endIndex = intervalStats.lastIndexOnOrBefore(endTime);
if (endIndex < 0) {
// All the stats start after this range ends, so nothing matches.
if (DEBUG) {
@@ -857,7 +857,7 @@ public class UsageStatsDatabase {
}
}
- int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
+ int startIndex = intervalStats.lastIndexOnOrBefore(beginTime);
if (startIndex < 0) {
// All the stats available have timestamps after beginTime, which means they all
// match.
@@ -899,7 +899,7 @@ public class UsageStatsDatabase {
int bestBucket = -1;
long smallestDiff = Long.MAX_VALUE;
for (int i = mSortedStatFiles.length - 1; i >= 0; i--) {
- final int index = mSortedStatFiles[i].closestIndexOnOrBefore(beginTimeStamp);
+ final int index = mSortedStatFiles[i].lastIndexOnOrBefore(beginTimeStamp);
int size = mSortedStatFiles[i].size();
if (index >= 0 && index < size) {
// We have some results here, check if they are better than our current match.
@@ -1523,7 +1523,7 @@ public class UsageStatsDatabase {
pw.println("Database Summary:");
pw.increaseIndent();
for (int i = 0; i < mSortedStatFiles.length; i++) {
- final TimeSparseArray<AtomicFile> files = mSortedStatFiles[i];
+ final LongSparseArray<AtomicFile> files = mSortedStatFiles[i];
final int size = files.size();
pw.print(UserUsageStatsService.intervalToString(i));
pw.print(" stats files: ");
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 92032086dc24..fd56b6ed1074 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -46,10 +46,10 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseIntArray;
-import android.util.TimeSparseArray;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -1024,7 +1024,7 @@ class UserUsageStatsService {
}
private void dumpFileDetailsForInterval(IndentingPrintWriter ipw, int interval) {
- final TimeSparseArray<AtomicFile> files = mDatabase.mSortedStatFiles[interval];
+ final LongSparseArray<AtomicFile> files = mDatabase.mSortedStatFiles[interval];
final int numFiles = files.size();
for (int i = 0; i < numFiles; i++) {
final long filename = files.keyAt(i);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 13945a119e6f..997015ff1c08 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -407,7 +407,9 @@ public class SoundTriggerService extends SystemService {
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"SoundTriggerSessionLogs for package: "
+ Objects.requireNonNull(originatorIdentity.packageName)
- + "#" + sessionId);
+ + "#" + sessionId
+ + " - " + originatorIdentity.uid
+ + "|" + originatorIdentity.pid);
return new SoundTriggerSessionStub(client,
newSoundTriggerHelper(moduleProperties, eventLogger), eventLogger);
}
@@ -428,7 +430,9 @@ public class SoundTriggerService extends SystemService {
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"SoundTriggerSessionLogs for package: "
+ Objects.requireNonNull(originatorIdentity.packageName) + "#"
- + sessionId);
+ + sessionId
+ + " - " + originatorIdentity.uid
+ + "|" + originatorIdentity.pid);
return new SoundTriggerSessionStub(client,
newSoundTriggerHelper(moduleProperties, eventLogger), eventLogger);
}
@@ -1801,7 +1805,9 @@ public class SoundTriggerService extends SystemService {
ServiceEvent.Type.ATTACH, identity.packageName + "#" + sessionId));
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"LocalSoundTriggerEventLogger for package: " +
- identity.packageName + "#" + sessionId);
+ identity.packageName + "#" + sessionId
+ + " - " + identity.uid
+ + "|" + identity.pid);
return new SessionImpl(newSoundTriggerHelper(underlyingModule, eventLogger, isTrusted),
client, eventLogger, identity);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
index 2f2cb594ff3a..55cbf29553f6 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
@@ -22,7 +22,7 @@ import android.os.HwBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -62,7 +62,7 @@ class DefaultHalFactory implements HalFactory {
android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ "/default";
if (ServiceManager.isDeclared(aidlServiceName)) {
- Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
() -> {
// This property needs to be defined in an init.rc script and
@@ -72,7 +72,7 @@ class DefaultHalFactory implements HalFactory {
}
// Fallback to soundtrigger-V2.x (HIDL).
- Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
return SoundTriggerHw2Compat.create(driver, () -> {
// This property needs to be defined in an init.rc script and
@@ -81,7 +81,7 @@ class DefaultHalFactory implements HalFactory {
}, mCaptureStateNotifier);
} else if (mockHal == USE_MOCK_HAL_V2) {
// Use V2 mock.
- Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
HwBinder.setTrebleTestingOverride(true);
try {
ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
@@ -89,7 +89,7 @@ class DefaultHalFactory implements HalFactory {
try {
driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
} catch (Exception e) {
- Log.e(TAG, "Failed to reboot mock HAL", e);
+ Slog.e(TAG, "Failed to reboot mock HAL", e);
}
}, mCaptureStateNotifier);
} finally {
@@ -100,14 +100,14 @@ class DefaultHalFactory implements HalFactory {
final String aidlServiceName =
android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ "/mock";
- Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
() -> {
try {
ServiceManager.waitForService(aidlServiceName).shellCommand(null,
null, null, new String[]{"reboot"}, null, null);
} catch (Exception e) {
- Log.e(TAG, "Failed to reboot mock HAL", e);
+ Slog.e(TAG, "Failed to reboot mock HAL", e);
}
});
} else {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index d195fbedcf2f..e3d64d4bf9db 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -17,7 +17,7 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.util.Log;
+import android.util.Slog;
import java.util.LinkedList;
import java.util.List;
@@ -94,7 +94,7 @@ class ExternalCaptureStateTracker implements ICaptureStateNotifier {
}
}
} catch (Exception e) {
- Log.e(TAG, "Exception caught while setting capture state", e);
+ Slog.e(TAG, "Exception caught while setting capture state", e);
}
}
@@ -102,7 +102,7 @@ class ExternalCaptureStateTracker implements ICaptureStateNotifier {
* Called by native code when the remote service died.
*/
private void binderDied() {
- Log.w(TAG, "Audio policy service died");
+ Slog.w(TAG, "Audio policy service died");
mNeedToConnect.release();
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
index c3e0a3cd0292..0f63347ccef8 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -27,7 +27,7 @@ import android.media.soundtrigger_middleware.PhraseRecognitionEventSys;
import android.media.soundtrigger_middleware.RecognitionEventSys;
import android.os.DeadObjectException;
import android.os.IBinder;
-import android.util.Log;
+import android.util.Slog;
import java.util.HashMap;
import java.util.Map;
@@ -227,10 +227,10 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
}
if (e.getCause() instanceof DeadObjectException) {
// Server is dead, no need to reboot.
- Log.e(TAG, "HAL died");
+ Slog.e(TAG, "HAL died");
throw new RecoverableException(Status.DEAD_OBJECT);
}
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ Slog.e(TAG, "Exception caught from HAL, rebooting HAL");
reboot();
throw e;
}
@@ -257,14 +257,14 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
synchronized (mModelStates) {
ModelState state = mModelStates.get(model);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ Slog.wtfStack(TAG, "Unexpected recognition event for model: " + model);
reboot();
return;
}
if (event.recognitionEvent.recognitionStillActive
&& event.recognitionEvent.status != RecognitionStatus.SUCCESS
&& event.recognitionEvent.status != RecognitionStatus.FORCED) {
- Log.wtfStack(TAG,
+ Slog.wtfStack(TAG,
"recognitionStillActive is only allowed when the recognition status "
+ "is SUCCESS");
reboot();
@@ -283,14 +283,14 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
synchronized (mModelStates) {
ModelState state = mModelStates.get(model);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ Slog.wtfStack(TAG, "Unexpected recognition event for model: " + model);
reboot();
return;
}
if (event.phraseRecognitionEvent.common.recognitionStillActive
&& event.phraseRecognitionEvent.common.status != RecognitionStatus.SUCCESS
&& event.phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
- Log.wtfStack(TAG,
+ Slog.wtfStack(TAG,
"recognitionStillActive is only allowed when the recognition status "
+ "is SUCCESS");
reboot();
@@ -309,13 +309,13 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
synchronized (mModelStates) {
ModelState state = mModelStates.get(modelHandle);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+ Slog.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
reboot();
return;
}
if (state == ModelState.ACTIVE) {
- Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+ Slog.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
reboot();
return;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 0390f034ab23..5e525e0d194e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -23,7 +23,7 @@ import android.media.soundtrigger.Properties;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.SoundModel;
import android.os.IBinder;
-import android.util.Log;
+import android.util.Slog;
import java.util.Objects;
@@ -172,7 +172,7 @@ public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
Watchdog() {
mTask = mTimer.createTask(() -> {
- Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+ Slog.e(TAG, "HAL deadline expired. Rebooting.", mException);
reboot();
}, TIMEOUT_MS);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index df2e9b41662b..730e92cb2aee 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -32,7 +32,7 @@ import android.os.IHwBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.system.OsConstants;
-import android.util.Log;
+import android.util.Slog;
import java.io.IOException;
import java.util.HashMap;
@@ -240,7 +240,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
try {
hidlModel.data.close();
} catch (IOException e) {
- Log.e(TAG, "Failed to close file", e);
+ Slog.e(TAG, "Failed to close file", e);
}
}
}
@@ -276,7 +276,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
try {
hidlModel.common.data.close();
} catch (IOException e) {
- Log.e(TAG, "Failed to close file", e);
+ Slog.e(TAG, "Failed to close file", e);
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index 3b800de2f30b..5a064da314c6 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
@@ -85,7 +85,7 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareIntern
try {
modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
} catch (Exception e) {
- Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
+ Slog.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 7ec2d9fd7b23..0b9ed8c20e8e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -36,7 +36,7 @@ import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -150,7 +150,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
e.getMessage());
}
- Log.wtf(TAG, "Unexpected exception", e);
+ Slog.wtf(TAG, "Unexpected exception", e);
throw new ServiceSpecificException(Status.INTERNAL_ERROR, e.getMessage());
}
@@ -701,7 +701,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
mCallback.onRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -719,7 +719,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
mCallback.onPhraseRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -734,7 +734,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
mCallback.onModelUnloaded(modelHandle);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -746,7 +746,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ Slog.e(TAG, "Client callback exception.", e);
}
}
@@ -761,7 +761,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ Slog.e(TAG, "Client callback exception.", e);
}
}
@@ -795,11 +795,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// Check if state updated unexpectedly to log race conditions.
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
if (cachedMap.get(entry.getKey()) != entry.getValue().activityState) {
- Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ Slog.e(TAG, "Unexpected state update in binderDied. Race occurred!");
}
}
if (mLoadedModels.size() != cachedMap.size()) {
- Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ Slog.e(TAG, "Unexpected state update in binderDied. Race occurred!");
}
try {
// Detach
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index e793f317d41f..45a7fafa90a7 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -31,7 +31,7 @@ import android.media.soundtrigger_middleware.RecognitionEventSys;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.HashMap;
@@ -136,7 +136,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
@Override
public void binderDied() {
- Log.w(TAG, "Underlying HAL driver died.");
+ Slog.w(TAG, "Underlying HAL driver died.");
List<ISoundTriggerCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<>(mActiveSessions.size());
@@ -270,7 +270,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
try {
mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
} catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ Slog.e(TAG, "Failed to release session.", ee);
}
throw e;
}
@@ -286,7 +286,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
checkValid();
Model loadedModel = new Model();
int result = loadedModel.load(model, audioSession);
- Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+ Slog.d(TAG, String.format("loadPhraseModel()->%d", result));
return result;
} catch (Exception e) {
// We must do this outside the lock, to avoid possible deadlocks with the remote
@@ -294,7 +294,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
try {
mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
} catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ Slog.e(TAG, "Failed to release session.", ee);
}
throw e;
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b5c1d7d9e5f4..c7f0c5f753db 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -36,8 +36,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Description of the response of a setup data call connection request.
@@ -172,63 +174,57 @@ public final class DataCallResponse implements Parcelable {
@Nullable List<InetAddress> dnsAddresses,
@Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu) {
- mCause = cause;
- mSuggestedRetryTime = suggestedRetryTime;
- mId = id;
- mLinkStatus = linkStatus;
- mProtocolType = protocolType;
- mInterfaceName = (interfaceName == null) ? "" : interfaceName;
- mAddresses = (addresses == null)
- ? new ArrayList<>() : new ArrayList<>(addresses);
- mDnsAddresses = (dnsAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(dnsAddresses);
- mGatewayAddresses = (gatewayAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
- mPcscfAddresses = (pcscfAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = mMtuV4 = mMtuV6 = mtu;
- mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
- mPduSessionId = PDU_SESSION_ID_NOT_SET;
- mDefaultQos = null;
- mQosBearerSessions = new ArrayList<>();
- mSliceInfo = null;
- mTrafficDescriptors = new ArrayList<>();
+ this(cause, suggestedRetryTime, id,
+ linkStatus, protocolType,
+ interfaceName == null ? "" : interfaceName,
+ addresses == null ? Collections.emptyList() : addresses,
+ dnsAddresses == null ? Collections.emptyList() : dnsAddresses,
+ gatewayAddresses == null ? Collections.emptyList() : gatewayAddresses,
+ pcscfAddresses == null ? Collections.emptyList() : pcscfAddresses,
+ mtu, mtu /* mtuV4 */, mtu /* mtuV6 */,
+ HANDOVER_FAILURE_MODE_LEGACY, PDU_SESSION_ID_NOT_SET,
+ null /* defaultQos */, Collections.emptyList() /* qosBearerSessions */,
+ null /* sliceInfo */,
+ Collections.emptyList() /* trafficDescriptors */);
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@LinkStatus int linkStatus, @ProtocolType int protocolType,
- @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
- @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
+ @NonNull String interfaceName, @NonNull List<LinkAddress> addresses,
+ @NonNull List<InetAddress> dnsAddresses, @NonNull List<InetAddress> gatewayAddresses,
+ @NonNull List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
+ @Nullable Qos defaultQos, @NonNull List<QosBearerSession> qosBearerSessions,
@Nullable NetworkSliceInfo sliceInfo,
- @Nullable List<TrafficDescriptor> trafficDescriptors) {
+ @NonNull List<TrafficDescriptor> trafficDescriptors) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
mLinkStatus = linkStatus;
mProtocolType = protocolType;
- mInterfaceName = (interfaceName == null) ? "" : interfaceName;
- mAddresses = (addresses == null)
- ? new ArrayList<>() : new ArrayList<>(addresses);
- mDnsAddresses = (dnsAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(dnsAddresses);
- mGatewayAddresses = (gatewayAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
- mPcscfAddresses = (pcscfAddresses == null)
- ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
+ mInterfaceName = interfaceName;
+ mAddresses = new ArrayList<>(addresses);
+ mDnsAddresses = new ArrayList<>(dnsAddresses);
+ mGatewayAddresses = new ArrayList<>(gatewayAddresses);
+ mPcscfAddresses = new ArrayList<>(pcscfAddresses);
mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosBearerSessions = (qosBearerSessions == null)
- ? new ArrayList<>() : new ArrayList<>(qosBearerSessions);
+ mQosBearerSessions = new ArrayList<>(qosBearerSessions);
mSliceInfo = sliceInfo;
- mTrafficDescriptors = (trafficDescriptors == null)
- ? new ArrayList<>() : new ArrayList<>(trafficDescriptors);
+ mTrafficDescriptors = new ArrayList<>(trafficDescriptors);
+
+ if (mLinkStatus == LINK_STATUS_ACTIVE
+ || mLinkStatus == LINK_STATUS_DORMANT) {
+ Objects.requireNonNull(
+ mInterfaceName, "Active data calls must be on a valid interface!");
+ if (mCause != DataFailCause.NONE) {
+ throw new IllegalStateException("Active data call must not have a failure!");
+ }
+ }
}
/** @hide */
@@ -241,24 +237,39 @@ public final class DataCallResponse implements Parcelable {
mProtocolType = source.readInt();
mInterfaceName = source.readString();
mAddresses = new ArrayList<>();
- source.readList(mAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
+ source.readList(mAddresses,
+ LinkAddress.class.getClassLoader(),
+ android.net.LinkAddress.class);
mDnsAddresses = new ArrayList<>();
- source.readList(mDnsAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mDnsAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mGatewayAddresses = new ArrayList<>();
- source.readList(mGatewayAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mGatewayAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mPcscfAddresses = new ArrayList<>();
- source.readList(mPcscfAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
+ source.readList(mPcscfAddresses,
+ InetAddress.class.getClassLoader(),
+ java.net.InetAddress.class);
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
- mDefaultQos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class);
+ mDefaultQos = source.readParcelable(Qos.class.getClassLoader(),
+ android.telephony.data.Qos.class);
mQosBearerSessions = new ArrayList<>();
- source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader(), android.telephony.data.QosBearerSession.class);
- mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader(), android.telephony.data.NetworkSliceInfo.class);
+ source.readList(mQosBearerSessions,
+ QosBearerSession.class.getClassLoader(),
+ android.telephony.data.QosBearerSession.class);
+ mSliceInfo = source.readParcelable(
+ NetworkSliceInfo.class.getClassLoader(),
+ android.telephony.data.NetworkSliceInfo.class);
mTrafficDescriptors = new ArrayList<>();
- source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class);
+ source.readList(mTrafficDescriptors,
+ TrafficDescriptor.class.getClassLoader(),
+ android.telephony.data.TrafficDescriptor.class);
}
/**
@@ -322,28 +333,36 @@ public final class DataCallResponse implements Parcelable {
* @return A list of addresses of this data connection.
*/
@NonNull
- public List<LinkAddress> getAddresses() { return mAddresses; }
+ public List<LinkAddress> getAddresses() {
+ return Collections.unmodifiableList(mAddresses);
+ }
/**
* @return A list of DNS server addresses, e.g., "192.0.1.3" or
* "192.0.1.11 2001:db8::1". Empty list if no dns server addresses returned.
*/
@NonNull
- public List<InetAddress> getDnsAddresses() { return mDnsAddresses; }
+ public List<InetAddress> getDnsAddresses() {
+ return Collections.unmodifiableList(mDnsAddresses);
+ }
/**
* @return A list of default gateway addresses, e.g., "192.0.1.3" or
* "192.0.1.11 2001:db8::1". Empty list if the addresses represent point to point connections.
*/
@NonNull
- public List<InetAddress> getGatewayAddresses() { return mGatewayAddresses; }
+ public List<InetAddress> getGatewayAddresses() {
+ return Collections.unmodifiableList(mGatewayAddresses);
+ }
/**
* @return A list of Proxy Call State Control Function address via PCO (Protocol Configuration
* Option) for IMS client.
*/
@NonNull
- public List<InetAddress> getPcscfAddresses() { return mPcscfAddresses; }
+ public List<InetAddress> getPcscfAddresses() {
+ return Collections.unmodifiableList(mPcscfAddresses);
+ }
/**
* @return MTU (maximum transmission unit) in bytes received from network. Zero or negative
@@ -404,7 +423,7 @@ public final class DataCallResponse implements Parcelable {
*/
@NonNull
public List<QosBearerSession> getQosBearerSessions() {
- return mQosBearerSessions;
+ return Collections.unmodifiableList(mQosBearerSessions);
}
/**
@@ -420,7 +439,7 @@ public final class DataCallResponse implements Parcelable {
*/
@NonNull
public List<TrafficDescriptor> getTrafficDescriptors() {
- return mTrafficDescriptors;
+ return Collections.unmodifiableList(mTrafficDescriptors);
}
@NonNull
@@ -461,18 +480,6 @@ public final class DataCallResponse implements Parcelable {
DataCallResponse other = (DataCallResponse) o;
- final boolean isQosBearerSessionsSame =
- (mQosBearerSessions == null || other.mQosBearerSessions == null)
- ? mQosBearerSessions == other.mQosBearerSessions
- : (mQosBearerSessions.size() == other.mQosBearerSessions.size()
- && mQosBearerSessions.containsAll(other.mQosBearerSessions));
-
- final boolean isTrafficDescriptorsSame =
- (mTrafficDescriptors == null || other.mTrafficDescriptors == null)
- ? mTrafficDescriptors == other.mTrafficDescriptors
- : (mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
- && mTrafficDescriptors.containsAll(other.mTrafficDescriptors));
-
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
&& mId == other.mId
@@ -493,42 +500,20 @@ public final class DataCallResponse implements Parcelable {
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& Objects.equals(mDefaultQos, other.mDefaultQos)
- && isQosBearerSessionsSame
+ && mQosBearerSessions.size() == other.mQosBearerSessions.size() // non-null
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions) // non-null
&& Objects.equals(mSliceInfo, other.mSliceInfo)
- && isTrafficDescriptorsSame;
+ && mTrafficDescriptors.size() == other.mTrafficDescriptors.size() // non-null
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); // non-null
}
@Override
public int hashCode() {
- // Generate order-independent hashes for lists
- int addressesHash = mAddresses.stream()
- .map(LinkAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int dnsAddressesHash = mDnsAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int gatewayAddressesHash = mGatewayAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int pcscfAddressesHash = mPcscfAddresses.stream()
- .map(InetAddress::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int qosBearerSessionsHash = mQosBearerSessions.stream()
- .map(QosBearerSession::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
- int trafficDescriptorsHash = mTrafficDescriptors.stream()
- .map(TrafficDescriptor::hashCode)
- .mapToInt(Integer::intValue)
- .sum();
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
- mInterfaceName, addressesHash, dnsAddressesHash, gatewayAddressesHash,
- pcscfAddressesHash, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, qosBearerSessionsHash, mSliceInfo, trafficDescriptorsHash);
+ mInterfaceName, Set.copyOf(mAddresses), Set.copyOf(mDnsAddresses),
+ Set.copyOf(mGatewayAddresses), Set.copyOf(mPcscfAddresses), mMtu, mMtuV4, mMtuV6,
+ mHandoverFailureMode, mPduSessionId, mDefaultQos, Set.copyOf(mQosBearerSessions),
+ mSliceInfo, Set.copyOf(mTrafficDescriptors));
}
@Override
@@ -616,15 +601,15 @@ public final class DataCallResponse implements Parcelable {
private @ProtocolType int mProtocolType;
- private String mInterfaceName;
+ private String mInterfaceName = "";
- private List<LinkAddress> mAddresses;
+ private List<LinkAddress> mAddresses = Collections.emptyList();
- private List<InetAddress> mDnsAddresses;
+ private List<InetAddress> mDnsAddresses = Collections.emptyList();
- private List<InetAddress> mGatewayAddresses;
+ private List<InetAddress> mGatewayAddresses = Collections.emptyList();
- private List<InetAddress> mPcscfAddresses;
+ private List<InetAddress> mPcscfAddresses = Collections.emptyList();
private int mMtu;
@@ -636,11 +621,11 @@ public final class DataCallResponse implements Parcelable {
private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
- private Qos mDefaultQos;
+ private @Nullable Qos mDefaultQos;
private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
- private NetworkSliceInfo mSliceInfo;
+ private @Nullable NetworkSliceInfo mSliceInfo;
private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
@@ -653,7 +638,9 @@ public final class DataCallResponse implements Parcelable {
/**
* Set data call fail cause.
*
- * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
+ * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error, which
+ * is the only valid value for data calls that are {@link LINK_STATUS_ACTIVE} or
+ * {@link LINK_STATUS_DORMANT}.
* @return The same instance of the builder.
*/
public @NonNull Builder setCause(@DataFailureCause int cause) {
@@ -722,10 +709,13 @@ public final class DataCallResponse implements Parcelable {
/**
* Set the network interface name.
*
- * @param interfaceName The network interface name (e.g. "rmnet_data1").
+ * @param interfaceName The network interface name (e.g. "rmnet_data1"). This value may not
+ * be null for valid data calls (those that are {@link LINK_STATUS_ACTIVE} or
+ * {@link LINK_STATUS_DORMANT}).
* @return The same instance of the builder.
*/
- public @NonNull Builder setInterfaceName(@NonNull String interfaceName) {
+ public @NonNull Builder setInterfaceName(@Nullable String interfaceName) {
+ if (interfaceName == null) interfaceName = "";
mInterfaceName = interfaceName;
return this;
}
@@ -737,6 +727,7 @@ public final class DataCallResponse implements Parcelable {
* @return The same instance of the builder.
*/
public @NonNull Builder setAddresses(@NonNull List<LinkAddress> addresses) {
+ Objects.requireNonNull(addresses);
mAddresses = addresses;
return this;
}
@@ -748,6 +739,7 @@ public final class DataCallResponse implements Parcelable {
* @return The same instance of the builder.
*/
public @NonNull Builder setDnsAddresses(@NonNull List<InetAddress> dnsAddresses) {
+ Objects.requireNonNull(dnsAddresses);
mDnsAddresses = dnsAddresses;
return this;
}
@@ -759,6 +751,7 @@ public final class DataCallResponse implements Parcelable {
* @return The same instance of the builder.
*/
public @NonNull Builder setGatewayAddresses(@NonNull List<InetAddress> gatewayAddresses) {
+ Objects.requireNonNull(gatewayAddresses);
mGatewayAddresses = gatewayAddresses;
return this;
}
@@ -771,6 +764,7 @@ public final class DataCallResponse implements Parcelable {
* @return The same instance of the builder.
*/
public @NonNull Builder setPcscfAddresses(@NonNull List<InetAddress> pcscfAddresses) {
+ Objects.requireNonNull(pcscfAddresses);
mPcscfAddresses = pcscfAddresses;
return this;
}
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index db369756c50e..346622f0f467 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -61,8 +61,11 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test {
options.setTestMethodName("testCollectAllApexInfo");
// Collect APEX package names from /apex, then pass them as expectation to be verified.
+ // The package names are collected from the find name with deduplication (NB: we used to
+ // deduplicate by dropping directory names with '@', but there's a DCLA case where it only
+ // has one directory with '@'. So we have to keep it and deduplicate the current way).
CommandResult result = getDevice().executeShellV2Command(
- "ls -d /apex/*/ |grep -v @ |grep -v /apex/sharedlibs |cut -d/ -f3");
+ "ls -d /apex/*/ |grep -v /apex/sharedlibs |cut -d/ -f3 |cut -d@ -f1 |sort |uniq");
assertTrue(result.getStatus() == CommandStatus.SUCCESS);
String[] packageNames = result.getStdout().split("\n");
for (var i = 0; i < packageNames.length; i++) {
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 8faedebb4601..feae3b79520f 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -108,6 +108,7 @@ android_test {
":FlickerTestsAppLaunch-src",
":FlickerTestsQuickswitch-src",
":FlickerTestsRotation-src",
+ ":FlickerTestsNotification-src",
],
}
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 1ede943a9fa2..ed63ec0a0e83 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -85,6 +85,8 @@
value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
<option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
new file mode 100644
index 000000000000..43f4ce9b4f6b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary activity over an existing split. By default the new secondary
+ * activity will stack over the previous secondary activity.
+ *
+ * Setup: From Activity A launch a split A|B.
+ *
+ * Transitions: Let B start C, expect C to cover B and end up in split A|C.
+ *
+ * To run this test: `atest FlickerTests:OpenThirdActivityOverSplitTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenThirdActivityOverSplitTest (flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ // Launch a split.
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ transitions {
+ testApp.launchThirdActivity(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /** Main activity remains visible throughout the transition. */
+ @Presubmit
+ @Test
+ fun mainActivityWindowAlwaysVisible() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ }
+
+ /** Main activity remains visible throughout the transition and takes up half of the screen. */
+ @Presubmit
+ @Test
+ fun mainActivityLayersAlwaysVisible() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+
+ flicker.assertLayersStart {
+ val display = this.entry.displays.firstOrNull { it.isOn && !it.isVirtual }
+ ?: error("No non-virtual and on display found")
+ val mainActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val secondaryActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .region
+ mainActivityRegion
+ .plus(secondaryActivityRegion)
+ .coversExactly(display.layerStackSpace)
+ }
+
+ flicker.assertLayersEnd {
+ val display = this.entry.displays.firstOrNull { it.isOn && !it.isVirtual }
+ ?: error("No non-virtual and on display found")
+ val mainActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val secondaryActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ secondaryActivityRegion.isEmpty()
+ val thirdActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ mainActivityRegion
+ .plus(thirdActivityRegion.region)
+ .coversExactly(display.layerStackSpace)
+ }
+ }
+
+ /** Third activity launches during the transition and covers up secondary activity. */
+ @Presubmit
+ @Test
+ fun thirdActivityWindowLaunchesIntoSplit() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) // expectation
+ }
+ }
+
+ /** Third activity launches during the transition and covers up secondary activity. */
+ @Presubmit
+ @Test
+ fun thirdActivityLayerLaunchesIntoSplit() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isInvisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ /** Assert the background animation layer is never visible during transition. */
+ @Presubmit
+ @Test
+ fun backgroundLayerNeverVisible() {
+ val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ flicker.assertLayers {
+ isInvisible(backgroundColorLayer)
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 865d5b4b771d..dbbc771809de 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index c1086332a656..566f393efaea 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ea9710c633b1..ed930fc8c236 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index d65555a97d78..49ed183c2ccf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index 351eb1e5d2c3..06beec19cbf0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -59,6 +59,23 @@ constructor(
.waitForAndVerify()
}
+ /** Clicks the button to launch a third activity over a secondary activity. */
+ fun launchThirdActivity(wmHelper: WindowManagerStateHelper) {
+ val launchButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "launch_third_activity_button")),
+ FIND_TIMEOUT
+ )
+ require(launchButton != null) { "Can't find launch third activity button on screen." }
+ launchButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_STOPPED)
+ .withActivityState(THIRD_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .waitForAndVerify()
+ }
+
/**
* Clicks the button to finishes the secondary activity launched through
* [launchSecondaryActivity], waits for the main activity to resume.
@@ -166,6 +183,9 @@ constructor(
val SECONDARY_ACTIVITY_COMPONENT =
ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT.toFlickerComponent()
+ val THIRD_ACTIVITY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.ThirdActivity.COMPONENT.toFlickerComponent()
+
val ALWAYS_EXPAND_ACTIVITY_COMPONENT =
ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT.toFlickerComponent()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index 2563bfb03f61..4fd4a61e4adc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 33302face72a..e39a578fd321 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index 45fb453f5df8..6d0b6f48d7c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 12ee7d0bff78..d2c38076e72d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index 1371fd7502d6..3e0958a27aaf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 46eb2571165a..2a2a3db12513 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -21,7 +21,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 1497e50641eb..eec6bfde8b9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index 9b6c136f54b3..ab6a1ea36222 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 4a1bd7e165ae..1bdb6e717b12 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -18,6 +18,8 @@ package com.android.server.wm.flicker.launch
import android.os.SystemClock
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.CameraAppHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -137,8 +139,14 @@ class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: LegacyFlickerTest)
@Postsubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(CAMERA_BACKGROUND)
+ )
+ }
+ }
@Postsubmit
@Test
@@ -161,5 +169,12 @@ class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: LegacyFlickerTest)
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+
+ private val CAMERA_BACKGROUND =
+ ComponentNameMatcher(
+ "Background for SurfaceView" +
+ "[com.google.android.GoogleCamera/" +
+ "com.google.android.apps.camera.legacy.app.activity.main.CameraActivity]"
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 39c8ca089dbd..4164c0d440c0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -121,7 +121,7 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
@FlakyTest(bugId = 265007895)
@Test
fun transitionHasColorBackground() {
- val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ val backgroundColorLayer = ComponentNameMatcher("", "animation-background")
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
flicker.assertLayers {
this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 64302831202e..7a2e74bdb8e7 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -200,6 +200,13 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity
+ android:name=".ActivityEmbeddingThirdActivity"
+ android:label="ActivityEmbedding Third"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="false"/>
+ <activity
android:name=".ActivityEmbeddingAlwaysExpandActivity"
android:label="ActivityEmbedding AlwaysExpand"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 239aba59f4a7..67314463161d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -27,4 +27,12 @@
android:layout_height="48dp"
android:text="Finish" />
+ <Button
+ android:id="@+id/launch_third_activity_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:onClick="launchThirdActivity"
+ android:text="Launch a third activity" />
+
</LinearLayout> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index 6e78750cdeee..dc21027bc99c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.testapp;
import android.app.Activity;
+import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
@@ -40,4 +41,9 @@ public class ActivityEmbeddingSecondaryActivity extends Activity {
}
});
}
+
+ public void launchThirdActivity(View view) {
+ startActivity(new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.ThirdActivity.COMPONENT));
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java
new file mode 100644
index 000000000000..3bd72818d894
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+
+/**
+ * Activity to be used also as a secondary activity to split with
+ * {@link ActivityEmbeddingMainActivity}.
+ */
+public class ActivityEmbeddingThirdActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_embedding_base_layout);
+ findViewById(R.id.root_activity_layout).setBackgroundColor(Color.RED);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 52106189840d..d84ac427f027 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -99,6 +99,12 @@ public class ActivityOptions {
FLICKER_APP_PACKAGE + ".ActivityEmbeddingSecondaryActivity");
}
+ public static class ThirdActivity {
+ public static final String LABEL = "ActivityEmbeddingThirdActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingThirdActivity");
+ }
+
public static class AlwaysExpandActivity {
public static final String LABEL = "ActivityEmbeddingAlwaysExpandActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index f4a04a163ebb..b6b99242c414 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -97,6 +97,8 @@ public final class NotificationTest {
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
// Do not run on TV. Direct Reply isn't supported on TV.
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ // Do not run on Wear. Direct Reply isn't supported on Wear.
+ assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
}
@After