summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--StubLibraries.bp49
-rw-r--r--apct-tests/perftests/windowmanager/Android.bp1
-rw-r--r--apct-tests/perftests/windowmanager/AndroidTest.xml8
-rw-r--r--apct-tests/perftests/windowmanager/README.md27
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java2
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java65
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java130
-rw-r--r--apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java45
-rw-r--r--api/current.txt1
-rw-r--r--api/removed.txt4
-rwxr-xr-xapi/system-current.txt4
-rw-r--r--api/test-current.txt5
-rw-r--r--core/java/android/app/ActivityManager.java24
-rw-r--r--core/java/android/app/ActivityManagerInternal.java9
-rw-r--r--core/java/android/app/ApplicationLoaders.java28
-rw-r--r--core/java/android/app/IActivityManager.aidl7
-rw-r--r--core/java/android/app/LoadedApk.java23
-rw-r--r--core/java/android/content/pm/PermissionInfo.java3
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java25
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java6
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java44
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java13
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java35
-rw-r--r--core/java/android/metrics/LogMaker.java2
-rw-r--r--core/java/android/os/Parcelable.java33
-rw-r--r--core/java/android/os/ParcelableHolder.java19
-rwxr-xr-xcore/java/android/provider/Settings.java6
-rw-r--r--core/java/android/text/Html.java2
-rwxr-xr-xcore/java/android/text/format/DateFormat.java34
-rw-r--r--core/java/android/text/format/DateUtils.java51
-rw-r--r--core/java/android/text/format/OWNERS3
-rw-r--r--core/java/android/text/format/Time.java2
-rw-r--r--core/java/android/text/format/TimeFormatter.java54
-rw-r--r--core/java/android/view/FocusFinder.java19
-rw-r--r--core/java/android/view/autofill/AutofillManager.java65
-rw-r--r--core/java/android/webkit/WebView.java2
-rw-r--r--core/java/android/webkit/WebViewClient.java10
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java4
-rw-r--r--core/java/android/widget/DayPickerView.java6
-rw-r--r--core/java/android/widget/Magnifier.java2
-rw-r--r--core/java/android/widget/NumberPicker.java5
-rw-r--r--core/java/android/widget/SimpleMonthView.java6
-rw-r--r--core/java/android/widget/TextClock.java28
-rw-r--r--core/java/com/android/internal/app/ProcessMap.java4
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java7
-rw-r--r--core/java/com/android/internal/listeners/ListenerExecutor.java16
-rw-r--r--core/java/com/android/internal/os/ClassLoaderFactory.java15
-rw-r--r--core/java/com/android/server/SystemConfig.java82
-rw-r--r--core/jni/android_os_Debug.cpp5
-rw-r--r--core/jni/android_os_VintfRuntimeInfo.cpp18
-rw-r--r--core/jni/com_android_internal_os_ClassLoaderFactory.cpp9
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/values-af/strings.xml3
-rw-r--r--core/res/res/values-am/strings.xml3
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-as/strings.xml3
-rw-r--r--core/res/res/values-az/strings.xml3
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml5
-rw-r--r--core/res/res/values-be/strings.xml3
-rw-r--r--core/res/res/values-bg/strings.xml3
-rw-r--r--core/res/res/values-bn/strings.xml3
-rw-r--r--core/res/res/values-bs/strings.xml7
-rw-r--r--core/res/res/values-cs/strings.xml3
-rw-r--r--core/res/res/values-da/strings.xml3
-rw-r--r--core/res/res/values-de/strings.xml3
-rw-r--r--core/res/res/values-el/strings.xml3
-rw-r--r--core/res/res/values-en-rAU/strings.xml3
-rw-r--r--core/res/res/values-en-rCA/strings.xml3
-rw-r--r--core/res/res/values-en-rGB/strings.xml3
-rw-r--r--core/res/res/values-en-rIN/strings.xml3
-rw-r--r--core/res/res/values-en-rXC/strings.xml2
-rw-r--r--core/res/res/values-es-rUS/strings.xml3
-rw-r--r--core/res/res/values-es/strings.xml3
-rw-r--r--core/res/res/values-et/strings.xml3
-rw-r--r--core/res/res/values-eu/strings.xml5
-rw-r--r--core/res/res/values-fa/strings.xml7
-rw-r--r--core/res/res/values-fi/strings.xml3
-rw-r--r--core/res/res/values-fr-rCA/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml3
-rw-r--r--core/res/res/values-gl/strings.xml3
-rw-r--r--core/res/res/values-gu/strings.xml3
-rw-r--r--core/res/res/values-hi/strings.xml3
-rw-r--r--core/res/res/values-hr/strings.xml3
-rw-r--r--core/res/res/values-hu/strings.xml3
-rw-r--r--core/res/res/values-hy/strings.xml3
-rw-r--r--core/res/res/values-in/strings.xml5
-rw-r--r--core/res/res/values-is/strings.xml3
-rw-r--r--core/res/res/values-it/strings.xml3
-rw-r--r--core/res/res/values-ja/strings.xml3
-rw-r--r--core/res/res/values-ka/strings.xml3
-rw-r--r--core/res/res/values-kk/strings.xml3
-rw-r--r--core/res/res/values-km/strings.xml3
-rw-r--r--core/res/res/values-kn/strings.xml3
-rw-r--r--core/res/res/values-ko/strings.xml3
-rw-r--r--core/res/res/values-ky/strings.xml5
-rw-r--r--core/res/res/values-lo/strings.xml3
-rw-r--r--core/res/res/values-lt/strings.xml3
-rw-r--r--core/res/res/values-lv/strings.xml3
-rw-r--r--core/res/res/values-mk/strings.xml5
-rw-r--r--core/res/res/values-ml/strings.xml3
-rw-r--r--core/res/res/values-mn/strings.xml3
-rw-r--r--core/res/res/values-mr/strings.xml3
-rw-r--r--core/res/res/values-ms/strings.xml3
-rw-r--r--core/res/res/values-my/strings.xml7
-rw-r--r--core/res/res/values-nb/strings.xml3
-rw-r--r--core/res/res/values-ne/strings.xml3
-rw-r--r--core/res/res/values-nl/strings.xml3
-rw-r--r--core/res/res/values-or/strings.xml3
-rw-r--r--core/res/res/values-pa/strings.xml3
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rBR/strings.xml3
-rw-r--r--core/res/res/values-pt-rPT/strings.xml17
-rw-r--r--core/res/res/values-pt/strings.xml3
-rw-r--r--core/res/res/values-ro/strings.xml3
-rw-r--r--core/res/res/values-ru/strings.xml3
-rw-r--r--core/res/res/values-si/strings.xml3
-rw-r--r--core/res/res/values-sk/strings.xml3
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-sq/strings.xml3
-rw-r--r--core/res/res/values-sr/strings.xml5
-rw-r--r--core/res/res/values-sv/strings.xml3
-rw-r--r--core/res/res/values-sw/strings.xml3
-rw-r--r--core/res/res/values-ta/strings.xml3
-rw-r--r--core/res/res/values-te/strings.xml3
-rw-r--r--core/res/res/values-th/strings.xml3
-rw-r--r--core/res/res/values-tl/strings.xml3
-rw-r--r--core/res/res/values-tr/strings.xml3
-rw-r--r--core/res/res/values-uk/strings.xml5
-rw-r--r--core/res/res/values-ur/strings.xml3
-rw-r--r--core/res/res/values-uz/strings.xml7
-rw-r--r--core/res/res/values-vi/strings.xml7
-rw-r--r--core/res/res/values-zh-rCN/strings.xml3
-rw-r--r--core/res/res/values-zh-rHK/strings.xml3
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/res/res/values-zu/strings.xml3
-rw-r--r--core/res/res/values/attrs_manifest.xml23
-rw-r--r--core/tests/coretests/src/android/graphics/PathTest.java4
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/platform.xml6
-rw-r--r--data/etc/privapp-permissions-platform.xml3
-rw-r--r--data/keyboards/Vendor_2e95_Product_7725.kl64
-rw-r--r--graphics/java/android/graphics/Region.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java)2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayController.java)16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java)72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java (renamed from packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java)10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java (renamed from packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java)32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java (renamed from packages/SystemUI/src/com/android/systemui/TransactionPool.java)9
-rw-r--r--libs/WindowManager/Shell/tests/Android.bp3
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java (renamed from libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java)6
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java)5
-rw-r--r--libs/hwui/JankTracker.cpp3
-rw-r--r--libs/hwui/JankTracker.h2
-rw-r--r--libs/hwui/hwui/Bitmap.cpp7
-rw-r--r--location/java/android/location/GpsStatus.java10
-rw-r--r--location/java/android/location/ILocationListener.aidl2
-rw-r--r--location/java/android/location/ILocationManager.aidl2
-rw-r--r--location/java/android/location/LocationListener.java15
-rw-r--r--location/java/android/location/LocationManager.java51
-rw-r--r--media/java/android/media/AudioDeviceInfo.java15
-rw-r--r--media/java/android/media/ExifInterface.java4
-rw-r--r--media/java/android/media/browse/MediaBrowser.java13
-rw-r--r--media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl4
-rw-r--r--media/java/android/service/media/MediaBrowserService.java2
-rw-r--r--non-updatable-api/current.txt1
-rw-r--r--non-updatable-api/removed.txt4
-rw-r--r--non-updatable-api/system-current.txt4
-rw-r--r--packages/CarSystemUI/res/layout/car_user_switching_dialog.xml1
-rw-r--r--packages/CarSystemUI/res/layout/super_notification_shade.xml80
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml46
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml4
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml15
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java20
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java46
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java519
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java137
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java283
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java19
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java48
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java23
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java256
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java10
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java4
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java6
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/AndroidManifest.xml11
-rw-r--r--packages/SystemUI/res/drawable/privacy_chip_bg.xml23
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml4
-rw-r--r--packages/SystemUI/res/layout/ongoing_privacy_chip.xml40
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml29
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml17
-rw-r--r--packages/SystemUI/res/values/strings.xml21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java179
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java186
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt296
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java412
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt290
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java153
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java130
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java101
-rw-r--r--services/Android.bp4
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java80
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java6
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java33
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java58
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java10
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java3
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java28
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java7
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java129
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java12
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java88
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java12
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java84
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java73
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssStatusProvider.java11
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java80
-rw-r--r--services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java2
-rw-r--r--services/core/java/com/android/server/location/util/AppOpsHelper.java84
-rw-r--r--services/core/java/com/android/server/location/util/Injector.java17
-rw-r--r--services/core/java/com/android/server/location/util/LocationAttributionHelper.java24
-rw-r--r--services/core/java/com/android/server/location/util/LocationPermissionsHelper.java107
-rw-r--r--services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java71
-rw-r--r--services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java67
-rw-r--r--services/core/java/com/android/server/location/util/SystemAppOpsHelper.java10
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java65
-rw-r--r--services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java87
-rw-r--r--services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java85
-rw-r--r--services/core/java/com/android/server/media/OWNERS1
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java195
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java52
-rw-r--r--services/core/java/com/android/server/pm/Settings.java17
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java13
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java9
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java16
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java48
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java9
-rw-r--r--services/core/java/com/android/server/wm/Task.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java61
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java49
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java73
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java170
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java15
-rw-r--r--tests/WindowInsetsTests/AndroidManifest.xml24
-rw-r--r--tests/WindowInsetsTests/res/layout/chat_activity.xml (renamed from tests/WindowInsetsTests/res/layout/window_inset_activity.xml)0
-rw-r--r--tests/WindowInsetsTests/res/layout/controller_activity.xml106
-rw-r--r--tests/WindowInsetsTests/res/layout/main_activity.xml37
-rw-r--r--tests/WindowInsetsTests/res/values/strings.xml11
-rw-r--r--tests/WindowInsetsTests/res/values/styles.xml8
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java (renamed from tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java)9
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java202
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java36
-rw-r--r--tools/aapt2/cmd/Compile.cpp20
-rw-r--r--tools/aapt2/cmd/Compile.h4
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp87
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp25
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
390 files changed, 8202 insertions, 3428 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 2bd5aee0cd24..0fe34fb650eb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -311,6 +311,15 @@ java_defaults {
compile_dex: true,
}
+java_defaults {
+ name: "android_stubs_dists_default",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android.jar",
+ },
+}
+
java_library_static {
name: "android_monolith_stubs_current",
srcs: [ ":api-stubs-docs" ],
@@ -346,7 +355,21 @@ java_library_static {
name: "android_system_monolith_stubs_current",
srcs: [ ":system-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
}
java_library_static {
@@ -378,14 +401,34 @@ java_library_static {
name: "android_test_stubs_current",
srcs: [ ":test-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_test.jar",
+ },
+ ],
}
java_library_static {
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
libs: ["sdk_system_29_android"],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
}
java_library_static {
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index f02cbcfc4daf..9e95a104af81 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -19,6 +19,7 @@ android_test {
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "platform-test-annotations",
],
test_suites: ["device-tests"],
platform_apis: true,
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
index 69d187f9419a..0a80cf96fba3 100644
--- a/apct-tests/perftests/windowmanager/AndroidTest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -21,9 +21,17 @@
<option name="test-file-name" value="WmPerfTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.wm" />
<option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.wm.WmPerfRunListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
new file mode 100644
index 000000000000..05fa6279a949
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -0,0 +1,27 @@
+## Window manager performance tests
+
+### Precondition
+To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
+is available, it is better to use the following instructions to lock CPU and GPU frequencies.
+```
+m perf-setup.sh
+PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
+adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
+adb shell chmod +x $PERF_SETUP_PATH
+adb shell $PERF_SETUP_PATH
+```
+
+### Example to run
+Use `atest`
+```
+atest WmPerfTests:RelayoutPerfTest -- \
+ --module-arg WmPerfTests:instrumentation-arg:kill-bg:=true
+```
+Use `am instrument`
+```
+adb shell am instrument -w -r -e class android.wm.RelayoutPerfTest \
+ -e listener android.wm.WmPerfRunListener \
+ -e kill-bg true \
+ com.android.perftests.wm/androidx.test.runner.AndroidJUnitRunner
+```
+* `kill-bg` is optional.
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index f04e55567520..cff5663e9d9e 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
+import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.IWindow;
@@ -52,6 +53,7 @@ import java.util.function.IntSupplier;
@RunWith(Parameterized.class)
@LargeTest
+@Presubmit
public class RelayoutPerfTest extends WindowManagerPerfTestBase {
private int mIteration;
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 655d2f7f8aa7..dc6245bf2c09 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -23,18 +23,15 @@ import android.app.KeyguardManager;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
-import android.os.BatteryManager;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
-import android.provider.Settings;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
-import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -43,7 +40,9 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
public class WindowManagerPerfTestBase {
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -56,21 +55,11 @@ public class WindowManagerPerfTestBase {
* is in /data because while enabling method profling of system server, it cannot write the
* trace to external storage.
*/
- static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
-
- private static int sOriginalStayOnWhilePluggedIn;
+ static final File BASE_OUT_PATH = new File("/data/local/WmPerfTests");
@BeforeClass
public static void setUpOnce() {
final Context context = getInstrumentation().getContext();
- final int stayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
- sOriginalStayOnWhilePluggedIn = -1;
- if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
- sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
- // Keep the device awake during testing.
- setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
- }
if (!BASE_OUT_PATH.exists()) {
executeShellCommand("mkdir -p " + BASE_OUT_PATH);
@@ -84,18 +73,6 @@ public class WindowManagerPerfTestBase {
.addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
- @AfterClass
- public static void tearDownOnce() {
- if (sOriginalStayOnWhilePluggedIn != -1) {
- setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
- }
- }
-
- private static void setStayOnWhilePluggedIn(int value) {
- executeShellCommand(String.format("settings put global %s %d",
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
- }
-
/**
* Executes shell command with reading the output. It may also used to block until the current
* command is completed.
@@ -124,6 +101,42 @@ public class WindowManagerPerfTestBase {
executeShellCommand("am profile stop system");
}
+ static void runWithShellPermissionIdentity(Runnable runnable) {
+ sUiAutomation.adoptShellPermissionIdentity();
+ try {
+ runnable.run();
+ } finally {
+ sUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ static class SettingsSession<T> implements AutoCloseable {
+ private final Consumer<T> mSetter;
+ private final T mOriginalValue;
+ private boolean mChanged;
+
+ SettingsSession(T originalValue, Consumer<T> setter) {
+ mOriginalValue = originalValue;
+ mSetter = setter;
+ }
+
+ void set(T value) {
+ if (Objects.equals(value, mOriginalValue)) {
+ mChanged = false;
+ return;
+ }
+ mSetter.accept(value);
+ mChanged = true;
+ }
+
+ @Override
+ public void close() {
+ if (mChanged) {
+ mSetter.accept(mOriginalValue);
+ }
+ }
+ }
+
/**
* Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
*/
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
new file mode 100644
index 000000000000..6eb85aacb4e8
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -0,0 +1,130 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.wm.WindowManagerPerfTestBase.executeShellCommand;
+import static android.wm.WindowManagerPerfTestBase.runWithShellPermissionIdentity;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.view.WindowManagerPolicyConstants;
+import android.wm.WindowManagerPerfTestBase.SettingsSession;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.policy.PhoneWindow;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+
+import java.util.List;
+
+/** Prepare the preconditions before running performance test. */
+public class WmPerfRunListener extends RunListener {
+
+ private static final String OPTION_KILL_BACKGROUND = "kill-bg";
+ private static final long KILL_BACKGROUND_WAIT_MS = 3000;
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private long mWaitPreconditionDoneMs = 500;
+
+ private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
+ value -> executeShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));
+
+ private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode),
+ value -> {
+ final String navOverlay;
+ switch (value) {
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
+ default:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
+ break;
+ }
+ executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
+ });
+
+ /** It only executes once before all tests. */
+ @Override
+ public void testRunStarted(Description description) {
+ final Bundle arguments = InstrumentationRegistry.getArguments();
+
+ // Use gesture navigation for consistency.
+ mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+ // Keep the device awake during testing.
+ mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);
+
+ runWithShellPermissionIdentity(() -> {
+ final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
+ atm.removeAllVisibleRecentTasks();
+ atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
+ });
+ PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
+
+ if (Boolean.parseBoolean(arguments.getString(OPTION_KILL_BACKGROUND))) {
+ runWithShellPermissionIdentity(this::killBackgroundProcesses);
+ mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
+ }
+ // Wait a while for the precondition setup to complete.
+ SystemClock.sleep(mWaitPreconditionDoneMs);
+ }
+
+ private void killBackgroundProcesses() {
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes == null) {
+ return;
+ }
+ for (RunningAppProcessInfo processInfo : processes) {
+ if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
+ && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
+ for (String pkg : processInfo.pkgList) {
+ am.forceStopPackage(pkg);
+ }
+ }
+ }
+ }
+
+ /** It only executes once after all tests. */
+ @Override
+ public void testRunFinished(Result result) {
+ mNavigationModeSetting.close();
+ mStayOnWhilePluggedInSetting.close();
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
new file mode 100644
index 000000000000..d018287986f2
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.test.filters;
+
+import android.wm.RelayoutPerfTest;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * A static filter to have the same signature as the one in frameworks/base/tests/utils/testutils/.
+ * This doesn't share the existing library because it doesn't support parameterized test.
+ */
+public class FrameworksTestsFilter extends Filter {
+
+ private boolean mShouldRun;
+
+ @Override
+ public boolean shouldRun(Description description) {
+ final Class<?> testClass = description.getTestClass();
+ // Parameterized test methods don't have the original information. So keep the last status
+ // that matches the target class.
+ mShouldRun = (mShouldRun && testClass == null) || testClass == RelayoutPerfTest.class;
+ return mShouldRun;
+ }
+
+ @Override
+ public String describe() {
+ return "Default filter";
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 7119902b07d3..1d2286b333bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24079,6 +24079,7 @@ package android.media {
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/api/removed.txt b/api/removed.txt
index 5a24f625d146..58dbeb8a7d3f 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/api/system-current.txt b/api/system-current.txt
index fa4543001f90..11db781dd42f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4172,10 +4172,6 @@ package android.media {
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/api/test-current.txt b/api/test-current.txt
index dc6626586efa..f1a3566f7b45 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20,6 +20,7 @@ package android {
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
@@ -83,6 +84,7 @@ package android.app {
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+ method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
@@ -3292,6 +3294,7 @@ package android.provider {
field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
+ field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
@@ -3300,6 +3303,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
+ field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final String AUTOFILL_SERVICE = "autofill_service";
field public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -3320,6 +3324,7 @@ package android.provider {
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7c4c19dde4d0..a88c6a890844 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1019,12 +1019,6 @@ public class ActivityManager {
return ActivityTaskManager.getMaxRecentTasksStatic();
}
- /** @removed */
- @Deprecated
- public static int getMaxNumPictureInPictureActions() {
- return 3;
- }
-
/**
* Information you can set and retrieve about the current activity within the recent task list.
*/
@@ -3739,7 +3733,8 @@ public class ActivityManager {
* manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
* </p>
*
- * @param state The state data
+ * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+ * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
*/
public void setProcessStateSummary(@Nullable byte[] state) {
try {
@@ -4941,4 +4936,19 @@ public class ActivityManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for use with CTS only.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.RESET_APP_ERRORS)
+ public void resetAppErrors() {
+ try {
+ getService().resetAppErrors();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7ba50cadd959..9e15c1fca077 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -103,17 +103,16 @@ public abstract class ActivityManagerInternal {
IBinder whitelistToken, long duration);
/**
- * Allows for a {@link PendingIntent} to be whitelisted to start activities from background.
+ * Allows a {@link PendingIntent} to start activities from background.
*/
public abstract void setPendingIntentAllowBgActivityStarts(
- IIntentSender target, IBinder whitelistToken, int flags);
+ IIntentSender target, IBinder allowlistToken, int flags);
/**
- * Voids {@link PendingIntent}'s privilege to be whitelisted to start activities from
- * background.
+ * Voids {@link PendingIntent}'s privilege to start activities from background.
*/
public abstract void clearPendingIntentAllowBgActivityStarts(IIntentSender target,
- IBinder whitelistToken);
+ IBinder allowlistToken);
/**
* Allow DeviceIdleController to tell us about what apps are whitelisted.
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index bac432e42318..15237beee805 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,17 +48,18 @@ public class ApplicationLoaders {
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null);
+ null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
+ libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
/**
@@ -77,14 +78,22 @@ public class ApplicationLoaders {
return loader;
}
+ // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
+ // Until that is supported, assume that all native shared libraries are used.
+ // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
+ // native shared libraries to the classloader.
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
- librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
+ librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
- String classLoaderName, List<ClassLoader> sharedLibraries) {
+ String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<String> nativeSharedLibraries) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@ public class ApplicationLoaders {
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
- targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
+ targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
+ nativeSharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -185,7 +195,8 @@ public class ApplicationLoaders {
// assume cached libraries work with current sdk since they are built-in
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
- null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
+ null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
+ null /* nativeSharedLibraries */);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -255,7 +266,8 @@ public class ApplicationLoaders {
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
+ cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
+ null /* nativeSharedLibraries */);
}
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index befd58824e63..3b6a7b8f7592 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -686,4 +686,11 @@ interface IActivityManager {
* Kills uid with the reason of permission change.
*/
void killUidForPermissionChange(int appId, int userId, String reason);
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ void resetAppErrors();
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f9b48e710148..1dc54ddbac4b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -412,6 +412,12 @@ public final class LoadedApk {
return;
}
for (SharedLibraryInfo lib : sharedLibraries) {
+ if (lib.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
List<String> paths = lib.getAllCodePaths();
outSeenPaths.addAll(paths);
for (String path : paths) {
@@ -696,6 +702,12 @@ public final class LoadedApk {
}
List<ClassLoader> loaders = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
+ if (info.isNative()) {
+ // Native shared lib doesn't contribute to the native lib search path. Its name is
+ // sent to libnativeloader and then the native shared lib is exported from the
+ // default linker namespace.
+ continue;
+ }
loaders.add(createSharedLibraryLoader(
info, isBundledApp, librarySearchPath, libraryPermittedPath));
}
@@ -898,10 +910,19 @@ public final class LoadedApk {
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
libraryPermittedPath);
+ List<String> nativeSharedLibraries = new ArrayList<>();
+ if (mApplicationInfo.sharedLibraryInfos != null) {
+ for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+ if (info.isNative()) {
+ nativeSharedLibraries.add(info.getName());
+ }
+ }
+ }
+
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befdcbaef..e990fd783498 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+ protLevel += "|companion";
+ }
if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
protLevel += "|retailDemo";
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d885fc6..862563706da7 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -79,6 +79,7 @@ public final class SharedLibraryInfo implements Parcelable {
private final long mVersion;
private final @Type int mType;
+ private final boolean mIsNative;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@ public final class SharedLibraryInfo implements Parcelable {
* @param type The lib type.
* @param declaringPackage The package that declares the library.
* @param dependentPackages The packages that depend on the library.
+ * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
*
* @hide
*/
public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
String name, long version, int type,
VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
- List<SharedLibraryInfo> dependencies) {
+ List<SharedLibraryInfo> dependencies, boolean isNative) {
mPath = path;
mPackageName = packageName;
mCodePaths = codePaths;
@@ -109,6 +111,16 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = declaringPackage;
mDependentPackages = dependentPackages;
mDependencies = dependencies;
+ mIsNative = isNative;
+ }
+
+ /** @hide */
+ public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+ String name, long version, int type,
+ VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+ List<SharedLibraryInfo> dependencies) {
+ this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
+ dependencies, false /* isNative */);
}
private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@ public final class SharedLibraryInfo implements Parcelable {
mDeclaringPackage = parcel.readParcelable(null);
mDependentPackages = parcel.readArrayList(null);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+ mIsNative = parcel.readBoolean();
}
/**
@@ -137,6 +150,15 @@ public final class SharedLibraryInfo implements Parcelable {
}
/**
+ * Tells whether this library is a native shared library or not.
+ *
+ * @hide
+ */
+ public boolean isNative() {
+ return mIsNative;
+ }
+
+ /**
* Gets the library name an app defines in its manifest
* to depend on the library.
*
@@ -320,6 +342,7 @@ public final class SharedLibraryInfo implements Parcelable {
parcel.writeParcelable(mDeclaringPackage, flags);
parcel.writeList(mDependentPackages);
parcel.writeTypedList(mDependencies);
+ parcel.writeBoolean(mIsNative);
}
private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2ee0ad67b108..872098c8689e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -92,6 +92,10 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addUsesOptionalLibrary(String libraryName);
+ ParsingPackage addUsesNativeLibrary(String libraryName);
+
+ ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage addUsesStaticLibrary(String libraryName);
ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage removeUsesOptionalLibrary(String libraryName);
+ ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
ParsingPackage setAnyDensity(int anyDensity);
ParsingPackage setAppComponentFactory(String appComponentFactory);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f932bc250e28..0c0dc313087e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -186,6 +186,13 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesNativeLibraries = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesOptionalNativeLibraries = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
@@ -669,6 +676,27 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
+ public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+ this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+
+ @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+ this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+ libraryName);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
@@ -982,6 +1010,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
sForInternedStringList.parcel(this.libraryNames, dest, flags);
sForInternedStringList.parcel(this.usesLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+ sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
@@ -1144,6 +1174,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
this.libraryNames = sForInternedStringList.unparcel(in);
this.usesLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+ this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+ this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
@@ -1417,6 +1449,18 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@NonNull
@Override
+ public List<String> getUsesNativeLibraries() {
+ return usesNativeLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesOptionalNativeLibraries() {
+ return usesOptionalNativeLibraries;
+ }
+
+ @NonNull
+ @Override
public List<String> getUsesStaticLibraries() {
return usesStaticLibraries;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 5b53c18b820c..7e0fe7dc41bf 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -230,6 +230,19 @@ public interface ParsingPackageRead extends Parcelable {
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3688f1bda979..e1f08f3e55a1 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -701,6 +701,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "uses-package":
// Dependencies for app installers; we don't currently try to
// enforce this.
@@ -2017,6 +2019,8 @@ public class ParsingPackageUtils {
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-native-library":
+ return parseUsesNativeLibrary(input, pkg, res, parser);
case "processes":
return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
case "uses-package":
@@ -2178,6 +2182,37 @@ public class ParsingPackageUtils {
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesNativeLibrary_name);
+ boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+ true);
+
+ if (lname != null) {
+ if (req) {
+ // Upgrade to treat as stronger constraint
+ pkg.addUsesNativeLibrary(lname)
+ .removeUsesOptionalNativeLibrary(lname);
+ } else {
+ // Ignore if someone already defined as required
+ if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+ pkg.addUsesOptionalNativeLibrary(lname);
+ }
+ }
+ }
+
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
throws IOException, XmlPullParserException {
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17206d9..d8a2082f4eae 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@ public class LogMaker {
/**
* Min required eventlog line length.
* See: android/util/cts/EventLogTest.java
- * Size checks enforced here are intended only as sanity checks;
+ * Size limits enforced here are intended only as a precaution;
* your logs may be truncated earlier. Please log responsibly.
*
* @hide
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 9b360edb7238..bedbba04255e 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -99,6 +99,35 @@ public interface Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface ContentsFlags {}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+ PARCELABLE_STABILITY_LOCAL,
+ PARCELABLE_STABILITY_VINTF,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Stability {}
+
+ /**
+ * Something that is not meant to cross compilation boundaries.
+ *
+ * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+ * since we don't currently have a notion of different local locations,
+ * higher stability levels are formed at higher levels.
+ *
+ * For instance, contained entirely within system partitions.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+ /**
+ * Something that is meant to be used between system and vendor.
+ * @see #getStability()
+ * @see ParcelableHolder
+ * @hide
+ */
+ public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
/**
* Descriptor bit used with {@link #describeContents()}: indicates that
* the Parcelable object's flattened representation includes a file descriptor.
@@ -129,8 +158,8 @@ public interface Parcelable {
* @return true if this parcelable is stable.
* @hide
*/
- default boolean isStable() {
- return false;
+ default @Stability int getStability() {
+ return PARCELABLE_STABILITY_LOCAL;
}
/**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index c37a2ff1112c..181f94b39841 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -37,10 +37,10 @@ public final class ParcelableHolder implements Parcelable {
* if {@link ParcelableHolder} contains value, otherwise, both are null.
*/
private Parcel mParcel;
- private boolean mIsStable = false;
+ private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
- public ParcelableHolder(boolean isStable) {
- mIsStable = isStable;
+ public ParcelableHolder(@Parcelable.Stability int stability) {
+ mStability = stability;
}
private ParcelableHolder() {
@@ -50,11 +50,11 @@ public final class ParcelableHolder implements Parcelable {
/**
* {@link ParcelableHolder}'s stability is determined by the parcelable
* which contains this ParcelableHolder.
- * For more detail refer to {@link Parcelable#isStable}.
+ * For more detail refer to {@link Parcelable#getStability}.
*/
@Override
- public boolean isStable() {
- return mIsStable;
+ public @Parcelable.Stability int getStability() {
+ return mStability;
}
@NonNull
@@ -81,7 +81,8 @@ public final class ParcelableHolder implements Parcelable {
* @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
*/
public synchronized boolean setParcelable(@Nullable Parcelable p) {
- if (p != null && this.isStable() && !p.isStable()) {
+ // a ParcelableHolder can only hold things at its stability or higher
+ if (p != null && this.getStability() > p.getStability()) {
return false;
}
mParcelable = p;
@@ -123,7 +124,7 @@ public final class ParcelableHolder implements Parcelable {
* Read ParcelableHolder from a parcel.
*/
public synchronized void readFromParcel(@NonNull Parcel parcel) {
- this.mIsStable = parcel.readBoolean();
+ this.mStability = parcel.readInt();
mParcelable = null;
@@ -145,7 +146,7 @@ public final class ParcelableHolder implements Parcelable {
@Override
public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeBoolean(this.mIsStable);
+ parcel.writeInt(this.mStability);
if (mParcel != null) {
parcel.writeInt(mParcel.dataSize());
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 500e476a7b80..346522a504c8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7614,6 +7614,8 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
/**
@@ -7621,6 +7623,8 @@ public final class Settings {
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
@@ -14180,6 +14184,8 @@ public final class Settings {
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
/**
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382256ae..ab19fa9a1256 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@ public class Html {
out.append(((ImageSpan) style[j]).getSource());
out.append("\">");
- // Don't output the dummy character underlying the image.
+ // Don't output the placeholder character underlying the image.
i = next;
}
if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 4fe6752be4d5..38e3b39f8cfc 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -26,8 +26,6 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -287,8 +285,10 @@ public class DateFormat {
*/
@UnsupportedAppUsage
public static String getTimeFormatString(Context context, int userHandle) {
- final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
+ return is24HourFormat(context, userHandle) ? dtpg.getBestPattern("Hm")
+ : dtpg.getBestPattern("hm");
}
/**
@@ -475,7 +475,6 @@ public class DateFormat {
SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
int count;
- LocaleData localeData = LocaleData.get(Locale.getDefault());
DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault());
String[] amPm = dfs.getAmPmStrings();
@@ -506,7 +505,7 @@ public class DateFormat {
break;
case 'c':
case 'E':
- replacement = getDayOfWeekString(localeData,
+ replacement = getDayOfWeekString(dfs,
inDate.get(Calendar.DAY_OF_WEEK), count, c);
break;
case 'K': // hour in am/pm (0-11)
@@ -534,8 +533,7 @@ public class DateFormat {
break;
case 'L':
case 'M':
- replacement = getMonthString(localeData,
- inDate.get(Calendar.MONTH), count, c);
+ replacement = getMonthString(dfs, inDate.get(Calendar.MONTH), count, c);
break;
case 'm':
replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
@@ -568,25 +566,29 @@ public class DateFormat {
}
}
- private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+ private static String getDayOfWeekString(DateFormatSymbols dfs, int day, int count, int kind) {
boolean standalone = (kind == 'c');
+ int context = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
+ final int width;
if (count == 5) {
- return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+ width = DateFormatSymbols.NARROW;
} else if (count == 4) {
- return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+ width = DateFormatSymbols.WIDE;
} else {
- return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+ width = DateFormatSymbols.ABBREVIATED;
}
+ return dfs.getWeekdays(context, width)[day];
}
- private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+ private static String getMonthString(DateFormatSymbols dfs, int month, int count, int kind) {
boolean standalone = (kind == 'L');
+ int monthContext = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
if (count == 5) {
- return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.NARROW)[month];
} else if (count == 4) {
- return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.WIDE)[month];
} else if (count == 3) {
- return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
+ return dfs.getMonths(monthContext, DateFormatSymbols.ABBREVIATED)[month];
} else {
// Calendar.JANUARY == 0, so add 1 to month.
return zeroPad(month+1, count);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 741312f2337e..ff08269a93a4 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
@@ -27,8 +28,6 @@ import android.icu.util.MeasureUnit;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -203,17 +202,23 @@ public class DateUtils
*/
@Deprecated
public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormatSymbols.getInstance();
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longWeekdayNames; break;
- case LENGTH_MEDIUM: names = d.shortWeekdayNames; break;
- case LENGTH_SHORT: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTER: names = d.shortWeekdayNames; break; // TODO
- case LENGTH_SHORTEST: names = d.tinyWeekdayNames; break;
- default: names = d.shortWeekdayNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT: // TODO
+ case LENGTH_SHORTER: // TODO
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[dayOfWeek];
+ return dfs.getWeekdays(DateFormatSymbols.FORMAT, width)[dayOfWeek];
}
/**
@@ -242,17 +247,23 @@ public class DateUtils
*/
@Deprecated
public static String getMonthString(int month, int abbrev) {
- LocaleData d = LocaleData.get(Locale.getDefault());
- String[] names;
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.getDefault());
+ final int width;
switch (abbrev) {
- case LENGTH_LONG: names = d.longMonthNames; break;
- case LENGTH_MEDIUM: names = d.shortMonthNames; break;
- case LENGTH_SHORT: names = d.shortMonthNames; break;
- case LENGTH_SHORTER: names = d.shortMonthNames; break;
- case LENGTH_SHORTEST: names = d.tinyMonthNames; break;
- default: names = d.shortMonthNames; break;
+ case LENGTH_LONG:
+ width = DateFormatSymbols.WIDE;
+ break;
+ case LENGTH_SHORTEST:
+ width = DateFormatSymbols.NARROW;
+ break;
+ case LENGTH_MEDIUM:
+ case LENGTH_SHORT:
+ case LENGTH_SHORTER:
+ default:
+ width = DateFormatSymbols.ABBREVIATED;
+ break;
}
- return names[month];
+ return dfs.getMonths(DateFormatSymbols.FORMAT, width)[month];
}
/**
diff --git a/core/java/android/text/format/OWNERS b/core/java/android/text/format/OWNERS
new file mode 100644
index 000000000000..32adc12bb901
--- /dev/null
+++ b/core/java/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 80c8ec852146..5b5c8548f281 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -361,7 +361,7 @@ public class Time {
*/
@Override
public String toString() {
- // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+ // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
// happens during debugging when the debugger calls toString().
TimeCalculator calculator = new TimeCalculator(this.timezone);
calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index 9393f36d1b6f..c71dfbbafd40 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,11 +22,10 @@ package android.text.format;
import android.content.res.Resources;
import android.icu.text.DateFormatSymbols;
+import android.icu.text.DecimalFormatSymbols;
import com.android.i18n.timezone.ZoneInfoData;
-import libcore.icu.LocaleData;
-
import java.nio.CharBuffer;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -53,17 +52,17 @@ class TimeFormatter {
private static final int DAYSPERNYEAR = 365;
/**
- * The Locale for which the cached LocaleData and formats have been loaded.
+ * The Locale for which the cached symbols and formats have been loaded.
*/
private static Locale sLocale;
private static DateFormatSymbols sDateFormatSymbols;
- private static LocaleData sLocaleData;
+ private static DecimalFormatSymbols sDecimalFormatSymbols;
private static String sTimeOnlyFormat;
private static String sDateOnlyFormat;
private static String sDateTimeFormat;
private final DateFormatSymbols dateFormatSymbols;
- private final LocaleData localeData;
+ private final DecimalFormatSymbols decimalFormatSymbols;
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
@@ -78,7 +77,7 @@ class TimeFormatter {
if (sLocale == null || !(locale.equals(sLocale))) {
sLocale = locale;
sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale);
- sLocaleData = LocaleData.get(locale);
+ sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale);
Resources r = Resources.getSystem();
sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
@@ -87,10 +86,10 @@ class TimeFormatter {
}
this.dateFormatSymbols = sDateFormatSymbols;
+ this.decimalFormatSymbols = sDecimalFormatSymbols;
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- localeData = sLocaleData;
}
}
@@ -172,12 +171,12 @@ class TimeFormatter {
}
private String localizeDigits(String s) {
- if (localeData.zeroDigit == '0') {
+ if (decimalFormatSymbols.getZeroDigit() == '0') {
return s;
}
int length = s.length();
- int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+ int offsetToLocalizedDigits = decimalFormatSymbols.getZeroDigit() - '0';
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
char ch = s.charAt(i);
@@ -220,35 +219,44 @@ class TimeFormatter {
char currentChar = formatBuffer.get(formatBuffer.position());
switch (currentChar) {
case 'A':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'a':
- modifyAndAppend((wallTime.getWeekDay() < 0
- || wallTime.getWeekDay() >= DAYSPERWEEK)
- ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+ modifyAndAppend(
+ (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+ ? "?"
+ : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getWeekDay() + 1],
modifier);
return false;
case 'B':
if (modifier == '-') {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?"
- : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.STANDALONE,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
} else {
- modifyAndAppend((wallTime.getMonth() < 0
- || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+ modifyAndAppend(
+ (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.WIDE)[wallTime.getMonth()],
modifier);
}
return false;
case 'b':
case 'h':
modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
- ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+ ? "?"
+ : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+ DateFormatSymbols.ABBREVIATED)[wallTime.getMonth()],
modifier);
return false;
case 'C':
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc6947fc4..713cfb48c95f 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@ public class FocusFinder {
}
final int count = focusables.size();
- if (count < 2) {
- return null;
- }
switch (direction) {
case View.FOCUS_FORWARD:
return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@ public class FocusFinder {
}
private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.lastIndexOf(focused);
if (position >= 0 && position + 1 < count) {
return focusables.get(position + 1);
}
}
- return focusables.get(0);
+ if (!focusables.isEmpty()) {
+ return focusables.get(0);
+ }
+ return null;
}
private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
- if (count < 2) {
- return null;
- }
if (focused != null) {
int position = focusables.indexOf(focused);
if (position > 0) {
return focusables.get(position - 1);
}
}
- return focusables.get(count - 1);
+ if (!focusables.isEmpty()) {
+ return focusables.get(count - 1);
+ }
+ return null;
}
private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fbfeda6f0bcc..b398cf6c9cb3 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -727,16 +727,22 @@ public final class AutofillManager {
mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
if (mSessionId != NO_SESSION) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
final AutofillClient client = getClient();
if (client != null) {
final SyncResultReceiver receiver = new SyncResultReceiver(
SYNC_CALLS_TIMEOUT_MS);
try {
- mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
- mServiceClient.asBinder(), receiver);
- final boolean sessionWasRestored = receiver.getIntResult() == 1;
+ boolean sessionWasRestored = false;
+ if (clientAdded) {
+ mService.restoreSession(mSessionId,
+ client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder(), receiver);
+ sessionWasRestored = receiver.getIntResult() == 1;
+ } else {
+ Log.w(TAG, "No service client for session " + mSessionId);
+ }
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -850,8 +856,8 @@ public final class AutofillManager {
if (isDisabledByServiceLocked()) {
return false;
}
- ensureServiceClientAddedIfNeededLocked();
- return mEnabled;
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+ return clientAdded ? mEnabled : false;
}
}
@@ -1007,7 +1013,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
@@ -1060,9 +1071,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// dont notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
@@ -1178,7 +1190,12 @@ public final class AutofillManager {
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+ if (!clientAdded) {
+ if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+ return callback;
+ }
if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
if (sVerbose) {
@@ -1241,9 +1258,10 @@ public final class AutofillManager {
@GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
- if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+ if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+ && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
@@ -1905,11 +1923,16 @@ public final class AutofillManager {
}
}
+ /**
+ * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
+ * if the AutofillManagerClient is added successfully or is already added. Otherwise,
+ * returns {@code false}.
+ */
@GuardedBy("mLock")
- private void ensureServiceClientAddedIfNeededLocked() {
+ private boolean tryAddServiceClientIfNeededLocked() {
final AutofillClient client = getClient();
if (client == null) {
- return;
+ return false;
}
if (mServiceClient == null) {
@@ -1924,7 +1947,10 @@ public final class AutofillManager {
flags = receiver.getIntResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.w(TAG, "Failed to initialize autofill: " + e);
- return;
+ // Reset the states initialized above.
+ mService.removeClient(mServiceClient, userId);
+ mServiceClient = null;
+ return false;
}
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1949,6 +1975,7 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
}
+ return true;
}
@GuardedBy("mLock")
@@ -1962,12 +1989,13 @@ public final class AutofillManager {
&& view.isLaidOut()
&& view.isVisibleToUser()) {
- ensureServiceClientAddedIfNeededLocked();
+ final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (sVerbose) {
- Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+ Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
+ + mServiceClient);
}
- if (mEnabled && !isClientDisablingEnterExitEvent()) {
+ if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
final AutofillId id = view.getAutofillId();
final AutofillValue value = view.getAutofillValue();
// Starts new session.
@@ -2692,6 +2720,7 @@ public final class AutofillManager {
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("context: "); pw.println(mContext);
+ pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
final AutofillClient client = getClient();
if (client != null) {
pw.print(pfx); pw.print("client: "); pw.print(client);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1c75232dc15c..052bca57d77c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1511,7 +1511,7 @@ public class WebView extends AbsoluteLayout
*
* @param hosts the list of hosts
* @param callback will be called with {@code true} if hosts are successfully added to the
- * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+ * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
* will be run on the UI thread
*/
public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88a36e3..7b6e1a370479 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -211,8 +212,9 @@ public class WebViewClient {
* when accessing private data or the view system.
*
* <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
- * Browsing checks. If this is undesired, whitelist the URL with {@link
- * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+ * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+ * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+ * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 1b899dbf6d03..33e64f4d37e9 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -38,8 +38,6 @@ import android.view.ViewGroup;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Locale;
/**
@@ -264,7 +262,7 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega
mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
DEFAULT_SHOW_WEEK_NUMBER);
mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
if (!CalendarView.parseDate(minDate, mMinDate)) {
CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 67fef13d23f2..7de2bd10482f 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -33,10 +33,6 @@ import com.android.internal.R;
import com.android.internal.widget.ViewPager;
import com.android.internal.widget.ViewPager.OnPageChangeListener;
-import libcore.icu.LocaleData;
-
-import java.util.Locale;
-
class DayPickerView extends ViewGroup {
private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
private static final int DEFAULT_START_YEAR = 1900;
@@ -86,7 +82,7 @@ class DayPickerView extends ViewGroup {
attrs, a, defStyleAttr, defStyleRes);
final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ Calendar.getInstance().getFirstDayOfWeek());
final String minDate = a.getString(R.styleable.CalendarView_minDate);
final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 23bbe69afafb..89206fda39f3 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1142,7 +1142,7 @@ public final class Magnifier {
bitmapRenderNode.setOutline(outline);
bitmapRenderNode.setClipToOutline(true);
- // Create a dummy draw, which will be replaced later with real drawing.
+ // Create a placeholder draw, which will be replaced later with real drawing.
final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
mContentWidth, mContentHeight);
try {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index baaf2a763487..3b482a8b2d54 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -34,6 +34,7 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.icu.text.DecimalFormatSymbols;
import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
@@ -61,8 +62,6 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -209,7 +208,7 @@ public class NumberPicker extends LinearLayout {
}
private static char getZeroDigit(Locale locale) {
- return LocaleData.get(locale).zeroDigit;
+ return DecimalFormatSymbols.getInstance(locale).getZeroDigit();
}
private java.util.Formatter createFormatter(Locale locale) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 61c77bc2f90e..695a253a04e0 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -27,6 +27,7 @@ import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.DisplayContext;
import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.SimpleDateFormat;
@@ -50,8 +51,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
-import libcore.icu.LocaleData;
-
import java.text.NumberFormat;
import java.util.Locale;
@@ -194,7 +193,8 @@ class SimpleMonthView extends View {
private void updateDayOfWeekLabels() {
// Use tiny (e.g. single-character) weekday names from ICU. The indices
// for this list correspond to Calendar days, e.g. SUNDAY is index 1.
- final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames;
+ final String[] tinyWeekdayNames = DateFormatSymbols.getInstance(mLocale)
+ .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
for (int i = 0; i < DAYS_IN_WEEK; i++) {
mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1];
}
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 6432438b6630..95c0e8658c57 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -30,6 +30,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.icu.text.DateTimePatternGenerator;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
@@ -43,8 +44,6 @@ import android.view.inspector.InspectableProperty;
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Calendar;
import java.util.TimeZone;
@@ -262,14 +261,11 @@ public class TextClock extends TextView {
}
private void init() {
- if (mFormat12 == null || mFormat24 == null) {
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
- if (mFormat12 == null) {
- mFormat12 = ld.timeFormat_hm;
- }
- if (mFormat24 == null) {
- mFormat24 = ld.timeFormat_Hm;
- }
+ if (mFormat12 == null) {
+ mFormat12 = getBestDateTimePattern("hm");
+ }
+ if (mFormat24 == null) {
+ mFormat24 = getBestDateTimePattern("Hm");
}
createTime(mTimeZone);
@@ -510,13 +506,11 @@ public class TextClock extends TextView {
private void chooseFormat() {
final boolean format24Requested = is24HourModeEnabled();
- LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
-
if (format24Requested) {
- mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
+ mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm"));
mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
} else {
- mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
+ mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm"));
mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
}
@@ -529,6 +523,12 @@ public class TextClock extends TextView {
}
}
+ private String getBestDateTimePattern(String skeleton) {
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ getContext().getResources().getConfiguration().locale);
+ return dtpg.getBestPattern(skeleton);
+ }
+
/**
* Returns a if not null, else return b if not null, else return c.
*/
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 81036f7ecba8..719c79b2540f 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -58,4 +58,8 @@ public class ProcessMap<E> {
public int size() {
return mMap.size();
}
+
+ public void clear() {
+ mMap.clear();
+ }
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d238d0eb916d..ea3d2de13ce6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -120,6 +120,13 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
+ // Flag related to Privacy Indicators
+
+ /**
+ * Whether the Permissions Hub is showing.
+ */
+ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
+
// Flags related to Assistant
/**
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index e78e32b829b3..9979e6056f50 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -40,7 +40,7 @@ public interface ListenerExecutor {
/**
* Called before this operation is to be run. Some operations may be canceled before they
* are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
- * will only be run if this method was run.
+ * will only be run if this method was run. This callback is invoked on the calling thread.
*/
default void onPreExecute() {}
@@ -49,7 +49,7 @@ public interface ListenerExecutor {
* RuntimeException, which will propagate normally. Implementations of
* {@link ListenerExecutor} have the option to override
* {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
- * intercept failures at the class level.
+ * intercept failures at the class level. This callback is invoked on the executor thread.
*/
default void onFailure(Exception e) {
// implementations should handle any exceptions that may be thrown
@@ -59,21 +59,24 @@ public interface ListenerExecutor {
/**
* Called after the operation is run. This method will always be called if
* {@link #onPreExecute()} is called. Success implies that the operation was run to
- * completion with no failures.
+ * completion with no failures. This callback may be invoked on the calling thread or
+ * executor thread.
*/
default void onPostExecute(boolean success) {}
/**
* Called after this operation is complete (which does not imply that it was necessarily
* run). Will always be called once per operation, no matter if the operation was run or
- * not. Success implies that the operation was run to completion with no failures.
+ * not. Success implies that the operation was run to completion with no failures. This
+ * callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
* May be override to handle operation failures at a class level. Will not be invoked in the
- * event of a RuntimeException, which will propagate normally.
+ * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+ * executor thread.
*/
default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
Exception exception) {
@@ -83,7 +86,8 @@ public interface ListenerExecutor {
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
- * match the value prior to the operation, then the operation is considered canceled.
+ * match the value prior to the operation, then the operation is considered canceled. If a null
+ * operation is supplied, nothing happens.
*/
default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
@Nullable ListenerOperation<TListener> operation) {
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index a18943c264f5..f83c5bdc4e28 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -101,7 +101,7 @@ public class ClassLoaderFactory {
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
}
@@ -111,18 +111,24 @@ public class ClassLoaderFactory {
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
classLoaderName, sharedLibraries);
+ String sonameList = "";
+ if (nativeSharedLibraries != null) {
+ sonameList = String.join(":", nativeSharedLibraries);
+ }
+
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
String errorMessage = createClassloaderNamespace(classLoader,
targetSdkVersion,
librarySearchPath,
libraryPermittedPath,
isNamespaceShared,
- dexPath);
+ dexPath,
+ sonameList);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (errorMessage != null) {
@@ -139,5 +145,6 @@ public class ClassLoaderFactory {
String librarySearchPath,
String libraryPermittedPath,
boolean isNamespaceShared,
- String dexPath);
+ String dexPath,
+ String sonameList);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 0eb3981ed598..c6a1153c747f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -49,10 +49,12 @@ import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -104,11 +106,17 @@ public class SystemConfig {
public final String name;
public final String filename;
public final String[] dependencies;
+ public final boolean isNative;
SharedLibraryEntry(String name, String filename, String[] dependencies) {
+ this(name, filename, dependencies, false /* isNative */);
+ }
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
+ this.isNative = isNative;
}
}
@@ -170,12 +178,6 @@ public class SystemConfig {
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
- // These are the packages that are whitelisted to be able to run as system user
- final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
- // These are the packages that should not run under system user
- final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
// These are the components that are enabled by default as VR mode listener services.
final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
@@ -309,14 +311,6 @@ public class SystemConfig {
return mLinkedApps;
}
- public ArraySet<String> getSystemUserWhitelistedApps() {
- return mSystemUserWhitelistedApps;
- }
-
- public ArraySet<String> getSystemUserBlacklistedApps() {
- return mSystemUserBlacklistedApps;
- }
-
public ArraySet<String> getHiddenApiWhitelistedApps() {
return mHiddenApiPackageWhitelist;
}
@@ -457,6 +451,7 @@ public class SystemConfig {
log.traceBegin("readAllPermissions");
try {
readAllPermissions();
+ readPublicNativeLibrariesList();
} finally {
log.traceEnd();
}
@@ -895,34 +890,6 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "system-user-whitelisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserWhitelistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
- case "system-user-blacklisted-app": {
- if (allowAppConfigs) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in "
- + permFile + " at " + parser.getPositionDescription());
- } else {
- mSystemUserBlacklistedApps.add(pkgname);
- }
- } else {
- logNotAllowedInPartition(name, permFile, parser);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
@@ -1513,6 +1480,37 @@ public class SystemConfig {
}
}
+ private void readPublicNativeLibrariesList() {
+ readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
+ String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
+ for (String dir : dirs) {
+ for (File f : (new File(dir)).listFiles()) {
+ String name = f.getName();
+ if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
+ readPublicLibrariesListFile(f);
+ }
+ }
+ }
+ }
+
+ private void readPublicLibrariesListFile(File listFile) {
+ try (BufferedReader br = new BufferedReader(new FileReader(listFile))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Line format is <soname> [abi]. We take the soname part.
+ String soname = line.trim().split(" ")[0];
+ SharedLibraryEntry entry = new SharedLibraryEntry(
+ soname, soname, new String[0], true);
+ mSharedLibraries.put(entry.name, entry);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read public libraries file " + listFile, e);
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3da2fa26ffe6..d6e8531fa6ca 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,9 +853,8 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz)
} cfg_state = CONFIG_UNKNOWN;
if (cfg_state == CONFIG_UNKNOWN) {
- auto runtime_info = vintf::VintfObject::GetInstance()
- ->getRuntimeInfo(false /* skip cache */,
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ auto runtime_info = vintf::VintfObject::GetInstance()->getRuntimeInfo(
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 9379ea6dcd10..b0271b9e92af 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,14 +29,12 @@ namespace android {
using vintf::RuntimeInfo;
using vintf::VintfObject;
-#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
- static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
- { \
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo( \
- false /* skipCache */, flags); \
- if (info == nullptr) return nullptr; \
- return env->NewStringUTF((cppString).c_str()); \
- } \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags) \
+ static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) { \
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags); \
+ if (info == nullptr) return nullptr; \
+ return env->NewStringUTF((cppString).c_str()); \
+ }
MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
@@ -54,8 +52,8 @@ MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbV
static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
{
- std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
- false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
+ std::shared_ptr<const RuntimeInfo> info =
+ VintfObject::GetRuntimeInfo(RuntimeInfo::FetchFlag::POLICYVERS);
if (info == nullptr) return 0;
return static_cast<jlong>(info->kernelSepolicyVersion());
}
diff --git a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index f8d41e4bef54..59c413ff58a6 100644
--- a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -28,16 +28,19 @@ static jstring createClassloaderNamespace_native(JNIEnv* env,
jstring librarySearchPath,
jstring libraryPermittedPath,
jboolean isShared,
- jstring dexPath) {
+ jstring dexPath,
+ jstring sonameList) {
return android::CreateClassLoaderNamespace(env, targetSdkVersion,
classLoader, isShared == JNI_TRUE,
dexPath,
- librarySearchPath, libraryPermittedPath);
+ librarySearchPath,
+ libraryPermittedPath,
+ sonameList);
}
static const JNINativeMethod g_methods[] = {
{ "createClassloaderNamespace",
- "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Ljava/lang/String;",
+ "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ef50fc8e2b6e..fe290f3e97e8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5034,6 +5034,11 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
+ <!-- @hide @TestApi Allows apps to reset the state of {@link com.android.server.am.AppErrors}.
+ <p>CTS tests will use UiAutomation.adoptShellPermissionIdentity() to gain access. -->
+ <permission android:name="android.permission.RESET_APP_ERRORS"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3b61c367e1bf..03c682fd74af 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk kieslys om oop te sluit of maak noodoproep."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk kieslys om oop te maak."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teken patroon om te ontsluit"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Keer terug na oproep"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Reg!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probeer weer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ce214fdf3ab..f80da896d8fa 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ለመክፈት ምናሌ ተጫን"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ለመክፈት ስርዓተ ጥለት ሳል"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ድንገተኛ አደጋ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ወደ ጥሪ ተመለስ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ትክክል!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"እንደገና ሞክር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e80720d9a2b8..41e3e26afc2c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -844,7 +844,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"رسم نقش لإلغاء التأمين"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"الطوارئ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"العودة إلى الاتصال"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحيح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 881a1278666c..c6c8bb502ba8 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক কৰিবলৈ মেনু টিপক।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জৰুৰীকালীন"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কললৈ উভতি যাওক"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"শুদ্ধ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আকৌ চেষ্টা কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6408ef629b36..f0ff88365173 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilidi açmaq üçün model çəkin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Təcili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zəngə qayıt"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Düzdür!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bir də cəhd edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2bcf9095cb04..8319da7ca7ee 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"NE"</string>
<string name="checked" msgid="9179896827054513119">"označeno je"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Dovršavanje radnje pomoću"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvorite pomoću"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index ed781fb25698..3540516fd9f3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Націсніце \"Меню\", каб разблакаваць, або зрабіце экстраны выклік."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Націсніце \"Меню\", каб разблакаваць."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намалюйце камбінацыю разблакоўкі, каб разблакаваць"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстранны выклік"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вярнуцца да выкліку"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правільна!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Паспрабуйце яшчэ раз"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f089d62707b5..6643bcfa822c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натиснете „Меню“, за да отключите."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Нарисувайте фигура, за да отключите"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Спешни случаи"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад към обаждането"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правилно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Опитайте отново"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 1ac213f01d6b..cbec4ac51d09 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক করতে বা জরুরি কল করতে মেনু টিপুন৷"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক করতে মেনু টিপুন৷"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক করতে প্যাটার্ন আঁকুন"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জরুরী"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"কলে ফিরুন"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"সঠিক!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আবার চেষ্টা করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f27441a667e7..87b02f553037 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite dugme Meni kako biste otključali uređaj ili obavili hitni poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite dugme Meni za otključavanje uređaja."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nacrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitno"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Povratak na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
@@ -1145,8 +1146,8 @@
<string name="capital_off" msgid="7443704171014626777">"Isključeno"</string>
<string name="checked" msgid="9179896827054513119">"označeno"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Izvrši akciju koristeći"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršite akciju koristeći %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Otvori koristeći"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Otvori koristeći %1$s"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 856176455150..c69ca37d0f1c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefon odemknete stisknutím tlačítka Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odblokujte pomocí gesta"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav nouze"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolat zpět"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správně!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zkusit znovu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1b3dab2352f4..fb100773dfa6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryk på Menu for at låse op."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn oplåsningsmønster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødsituation"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbage til opkald"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 296200f8004c..4adcf0ae48ec 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Zum Entsperren die Menütaste drücken"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Muster zum Entsperren zeichnen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Notfall"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zurück zum Anruf"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Erneut versuchen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e790c9a071a9..23895f4fbf57 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Σχεδιασμός μοτίβου για ξεκλείδωμα"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Κλήση έκτακτης ανάγκης"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Επιστροφή στην κλήση"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Προσπαθήστε ξανά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0f1566b10347..0484ccd8abaa 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 84b3ce1ffc22..9981c1212123 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d9a2bdba5f10..02cdf69a2cd8 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1209f2bc4318..3b18aca951f7 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7f29fd463403..6503f7ce65a7 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -832,7 +832,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‎Press Menu to unlock or place emergency call.‎‏‎‎‏‎"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎Press Menu to unlock.‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎Draw pattern to unlock‎‏‎‎‏‎"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎Emergency‎‏‎‎‏‎"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎Emergency call‎‏‎‎‏‎"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎Return to call‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎Correct!‎‏‎‎‏‎"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎Try again‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f8ead2795a9a..7e8a89aa5a94 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Presionar Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar el patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regresar a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 741c89f0e5d9..02aafa75945a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d724046c2a73..813629417a8e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Vajutage avamiseks menüüklahvi."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Avamiseks joonistage muster"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hädaabi"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kõne juurde tagasi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Õige."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Proovige uuesti"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 24d3eef619e8..1b88f2708782 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Desblokeatzeko, sakatu Menua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desblokeatzeko, marraztu eredua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Larrialdi-deiak"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Itzuli deira"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Eredua zuzena da!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Saiatu berriro"</string>
@@ -846,7 +847,7 @@
<string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Sartu SIM txartela."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"SIM txartela falta da edo ezin da irakurri. Sartu SIM txartel bat."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"SIM txartela hondatuta dago."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela behin betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Aurreko pista"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Hurrengo pista"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b0b533858b3a..506a7ee79dcb 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"برای بازگشایی قفل روی منو فشار دهید."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"الگو را بکشید تا قفل آن باز شود"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"اضطراری"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"بازگشت به تماس"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح است!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوباره امتحان کنید"</string>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"خاموش"</string>
<string name="checked" msgid="9179896827054513119">"علامت‌زده‌شده"</string>
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
- <string name="whichApplication" msgid="5432266899591255759">"تکمیل عملکرد با استفاده از"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل عملکرد با استفاده از %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"باز کردن با"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"‏باز کردن با %1$s"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 815daaa5d7ae..01db781a6ccd 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Poista lukitus painamalla Valikko-painiketta."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Poista lukitus piirtämällä kuvio"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hätäpuhelu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Palaa puheluun"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Oikein!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Yritä uudelleen"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d3323d733824..f5adc779e082 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgence"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"C\'est exact!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Réessayer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4a1646f8ba30..8880dc63428f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgences"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Combinaison correcte !"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Veuillez réessayer."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 54e17faf9b4a..dffca1c2abcd 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Preme Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Crea o padrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emerxencia"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver á chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Téntao de novo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index be17cfd03115..784e85f9a174 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"અનલૉક કરવા માટે પૅટર્ન દોરો."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ઇમર્જન્સી"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"કૉલ પર પાછા ફરો"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"સાચું!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ફરી પ્રયાસ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 97118f8bffae..82a106295acb 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"लॉक खोलने के लिए मेन्यू दबाएं या आपातलकालीन कॉल करें."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करने के लिए आकार आरेखित करें"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपातकाल"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉल पर वापस लौटें"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फिर से कोशिश करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index a1b303ee7cd4..f1bcd398c65f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite Izbornik za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iscrtajte uzorak za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Uzvrati poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ff0d4709c970..3106371a2a33 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"A feloldáshoz nyomja meg a Menü gombot."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rajzolja le a mintát a feloldáshoz"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Segélyhívás"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Hívás folytatása"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Helyes!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Próbálja újra"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 05c447d50f7b..85afc27bb14c 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Հավաքեք սխեման` ապակողպելու համար"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Շտապ կանչ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Վերադառնալ զանգին"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ճիշտ է:"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Կրկին փորձեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index a6f68d171ec4..8ec1f50eaceb 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Buat pola untuk membuka"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Darurat"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Perbaiki!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"MATI"</string>
<string name="checked" msgid="9179896827054513119">"dicentang"</string>
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Tindakan lengkap menggunakan"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Buka dengan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 92f6f2488d18..1b0c2fe24521 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ýttu á valmyndartakkann til að taka úr lás."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teiknaðu mynstur til að taka úr lás"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Neyðarsímtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Aftur í símtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rétt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Reyndu aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8f857fa32c73..d2bf62d94a40 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Premi Menu per sbloccare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Traccia la sequenza di sblocco"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergenza"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Torna a chiamata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corretta."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Riprova"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 27889fb9b512..f9674bd0db11 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"MENUキーでロック解除(または緊急通報)"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"MENUキーでロック解除"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"パターンを入力"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急通報"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"通話に戻る"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"一致しました"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"もう一度お試しください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6547185c4beb..3d1e9c799a4d 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"განსაბლოკად დახატეთ ნიმუში"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"საგანგებო სამსახურები"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ზარზე დაბრუნება"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"სწორია!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"კიდევ სცადეთ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3d53912ad62b..38a50abfe6e7 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ашу үшін Мәзір пернесін басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Бекітпесін ашу үшін кескінді сызыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Төтенше жағдай"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Қоңырауға оралу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 760d256ca62c..4e646c5d2a07 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ​ ឬ​ហៅ​ពេល​អាសន្ន។"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ។"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"គូរ​លំនាំ ដើម្បី​ដោះ​សោ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"បន្ទាន់"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ត្រឡប់​ទៅ​ការ​ហៅ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ត្រឹមត្រូវ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ព្យាយាម​ម្ដង​ទៀត"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 6513481089ad..8d742156ca2c 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಪ್ಯಾಟರ್ನ್ ಚಿತ್ರಿಸಿ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ತುರ್ತು"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ಕರೆಗೆ ಹಿಂತಿರುಗು"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ಸರಿಯಾಗಿದೆ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index db8d379c4083..5d960dcc0aa7 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"잠금해제를 위해 패턴 그리기"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"긴급 전화"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"통화로 돌아가기"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"다시 시도"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index c208396b278a..c97324f738b3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Кулпуну ачуу үчүн, үлгүнү тартыңыз"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Тез жардам"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Чалууга кайтуу"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Туура!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дагы аракет кылыңыз"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ӨЧҮК"</string>
<string name="checked" msgid="9179896827054513119">"белгиленген"</string>
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Аракет колдонууну бүтүрүү"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Төмөнкү менен ачуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index f388cfaed0ca..c1b3fe473aed 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ແຕ້ມຮູບແບບເພື່ອປົດລັອກ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ສຸກ​ເສີນ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ກັບໄປຫາການໂທ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ຖືກຕ້ອງ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ລອງໃໝ່ອີກຄັ້ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d68cd8d3c822..fc5709c0e089 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Paspauskite „Meniu“, jei norite atrakinti."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nustatyti modelį, kad atrakintų"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Skambutis pagalbos numeriu"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"grįžti prie skambučio"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Teisingai!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bandykite dar kartą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9462c227a3cf..d780b14ae11a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Zīmējiet kombināciju, lai atbloķētu."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Ārkārtas situācija"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Atpakaļ pie zvana"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pareizi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Mēģināt vēlreiz"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index aee1fca01cb1..b50c608190e4 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притиснете „Мени“ за да се отклучи."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Употребете ја шемата за да се отклучи"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Итен случај"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Врати се на повик"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ИСКЛУЧЕНО"</string>
<string name="checked" msgid="9179896827054513119">"штиклирано"</string>
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Заврши дејство со"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отвори со"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 375cb98b0c4f..b769fd23ab1e 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"അൺലോക്ക് ചെയ്യാൻ പാറ്റേൺ വരയ്‌ക്കുക"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"എമർജൻസി"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"കോളിലേക്ക് മടങ്ങുക"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ശരി!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"വീണ്ടും ശ്രമിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1472c4e9f178..7b41501e6a0f 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Тайлах хээгээ зурна уу"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Яаралтай тусламж"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Дуудлагаруу буцах"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Зөв!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дахин оролдох"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6695de6c7a33..577803334c33 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलॉक करण्‍यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलॉक करण्यासाठी मेनू दाबा."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करण्यासाठी पॅटर्न काढा"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आणीबाणी"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉलवर परत या"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"अचूक!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"पुन्हा प्रयत्न करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c8f37c305ff7..c30bcfc1800f 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka kunci."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Lukiskan corak untuk membuka kunci"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Kecemasan"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Betul!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Cuba lagi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index dcb4e3cfc634..243357eca3c2 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ဖွင့်ရန်ပုံစံဆွဲပါ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"အရေးပေါ်"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ခေါ်ဆိုမှုထံပြန်သွားရန်"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"မှန်ပါသည်"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
@@ -1102,7 +1103,7 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"ပြန်ဖျက်ရန်"</string>
+ <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
<string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"ပိတ်"</string>
<string name="checked" msgid="9179896827054513119">"အမှန်ခြစ်ပြီး"</string>
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
- <string name="whichApplication" msgid="5432266899591255759">"အသုံးပြု၍ ဆောင်ရွက်မှုအားပြီးဆုံးစေခြင်း"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"...ဖြင့် ဖွင့်မည်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6926d659ed3f..0a31b49c555c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Trykk på menyknappen for å låse opp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn mønster for å låse opp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødssituasjon"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbake til samtale"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv på nytt"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index c9ac6ec47001..9712b480b97b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपतकालीन"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 904c90665229..d1a342c99590 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk op \'Menu\' om te ontgrendelen."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Patroon tekenen om te ontgrendelen"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Terug naar gesprek"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Juist!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Opnieuw proberen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 62505a31a169..84815f7a6133 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ କିମ୍ବା ଜରୁରୀକାଳୀନ କଲ୍‌ କରନ୍ତୁ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ଅନଲକ୍‌ କରିବା ପାଇଁ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ଜରୁରୀକାଳୀନ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"କଲ୍‌କୁ ଫେରନ୍ତୁ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 346ee9a476c7..c29308fd4c5b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪੈਟਰਨ ਡ੍ਰਾ ਕਰੋ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ਸੰਕਟਕਾਲ"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ਕਾਲ ਤੇ ਵਾਪਸ ਜਾਓ"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ਸਹੀ!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7d2795e2db0f..df56457da65a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Naciśnij Menu, aby odblokować."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Narysuj wzór, aby odblokować"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Alarmowe"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Powrót do połączenia"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4fec5b3779a2..3ecf28d34ffd 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5f6fb5cf553c..dcc8e17609ac 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -286,7 +286,7 @@
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"Demonstração para retalho"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string>
- <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicações que estão a consumir bateria"</string>
+ <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Prima Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhar padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regressar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tentar novamente"</string>
@@ -1153,7 +1154,7 @@
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagem"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Utilizar por predefinição para esta ação."</string>
<string name="use_a_different_app" msgid="4987790276170972776">"Utilizar outra app"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Escolha uma ação"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"Escolher uma app para o dispositivo USB"</string>
<string name="noApplications" msgid="1186909265235544019">"Nenhuma app pode efetuar esta ação."</string>
@@ -1181,7 +1182,7 @@
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> foi originalmente iniciado."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Escala"</string>
<string name="screen_compat_mode_show" msgid="5080361367584709857">"Mostrar sempre"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> não suporta a definição de Tamanho do ecrã atual e pode ter um comportamento inesperado."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar sempre"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi concebida para uma versão incompatível do SO Android e pode ter um comportamento inesperado. Pode estar disponível uma versão atualizada da app."</string>
@@ -1274,7 +1275,7 @@
<string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Enviar"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Cancelar"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Memorizar a minha escolha"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Aplicações"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permitir Sempre"</string>
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nunca Permitir"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"Cartão SIM removido"</string>
@@ -1284,8 +1285,8 @@
<string name="sim_added_message" msgid="6602906609509958680">"Reinicie o aparelho para aceder à rede de telemóvel."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar o serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Transfira a app do operador para ativar o seu novo SIM."</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Transfira a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
+ <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descarregue a app do operador para ativar o seu novo SIM."</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarregue a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Transferir app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo SIM inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -2035,7 +2036,7 @@
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta app não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Página inicial"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Anterior"</string>
- <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplicações recentes"</string>
+ <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Apps recentes"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notificações"</string>
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Definições rápidas"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Caixa de diálogo de energia"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4fec5b3779a2..3ecf28d34ffd 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a0e35985b142..64936689ee6e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1a65847fecf4..07abf6dbb28d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Для разблокировки нажмите \"Меню\"."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Введите графический ключ"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстренный вызов"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вернуться к вызову"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторите попытку"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index db759f62b8ae..d494a36520bf 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"අගුළු ඇරීමට රටාව අඳින්න"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"හදිසි"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"ඇමතුම වෙත නැවත යන්න"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"නිවැරදියි!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"නැවත උත්සාහ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5f80364368eb..52c53015743b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefón odomknete stlačením tlačidla Menu."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odomknite nakreslením vzoru"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav tiesne"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolať späť"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správne!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Skúsiť znova"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4e3705976e5f..a43c900fb585 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Če želite odkleniti, pritisnite meni."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Če želite odkleniti, narišite vzorec"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Klic v sili"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazaj na klic"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pravilno."</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Poskusi znova"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 30e75558bb76..625962fb9bd1 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Shtyp \"Meny\" për të shkyçur."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vizato modelin për ta shkyçur"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgjenca"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kthehu te telefonata"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Saktë!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Provo sërish"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6890b800ac1e..6101dac65f95 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -835,7 +835,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Хитне службе"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1145,7 +1146,7 @@
<string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
<string name="checked" msgid="9179896827054513119">"означено је"</string>
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Довршавање радње помоћу"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Отворите помоћу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 87b087203a6a..e30bb14384f9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryck på Menu för att låsa upp eller ringa nödsamtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryck på Menu för att låsa upp."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rita grafiskt lösenord för att låsa upp"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nödsamtal"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tillbaka till samtal"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Försök igen"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbf4ccae2e4a..4e4c0f656c9f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Bonyeza Menyu ili kufungua."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Chora ruwaza ili kufungua"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Dharura"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Rudi kwa kupiga simu"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Sahihi!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Jaribu tena"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 731a796a9d08..a617248071e0 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"திறக்க, மெனுவை அழுத்தவும்."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"திறக்க வடிவத்தை வரையவும்"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"அவசர அழைப்பு"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"அழைப்பிற்குத் திரும்பு"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"சரி!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"மீண்டும் முயற்சிக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a2ec7872113b..794fec85bcf7 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"అన్‌లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"అన్‌లాక్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"అన్‌లాక్ చేయడానికి నమూనాను గీయండి"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"అత్యవసరం"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"కాల్‌కు తిరిగి వెళ్లు"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ప్రయత్నించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 6773014ec2f8..3deb9b2b7b82 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"กด เมนู เพื่อปลดล็อก"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"วาดรูปแบบเพื่อปลดล็อก"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"เหตุฉุกเฉิน"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"กลับสู่การโทร"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ถูกต้อง!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ลองอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index da3e4373382a..ccda1c46059e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pindutin ang Menu upang i-unlock."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iguhit ang pattern upang i-unlock"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Bumalik sa tawag"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tama!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Subukang muli"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bf17f6c360d4..85c69860eb81 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmak için Menü\'ye basın."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilit açmak için deseni çizin"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Acil durum çağrısı"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Çağrıya dön"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Doğru!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tekrar deneyin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 9464efa043f0..8ae20a50f7f3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -838,7 +838,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натисн. меню, щоб розбл."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намал. ключ, щоб розбл."</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Екстрений виклик"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Поверн. до дзвін."</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторіть спробу"</string>
@@ -1165,7 +1166,7 @@
<string name="capital_off" msgid="7443704171014626777">"ВИМК"</string>
<string name="checked" msgid="9179896827054513119">"вибрано"</string>
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Завершити дію за доп."</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Відкрити за допомогою"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 15a1fe7d45f0..94c6cd450a6c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"غیر مقفل کرنے کیلئے پیٹرن کو ڈرا کریں"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ہنگامی"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"کال پر واپس جائیں"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوبارہ کوشش کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 76d2c609b63d..74676dfcf226 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Qulfni ochish uchun grafik kalitni chizing"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Favqulodda chaqiruv"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Qo‘ng‘iroqni qaytarish"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"To‘g‘ri!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Qaytadan urining"</string>
@@ -1125,7 +1126,7 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"belgilandi"</string>
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Ilovani tanlang"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Ochish…"</string>
@@ -1548,7 +1549,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"Brauzer ishga tushirilsinmi?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"Har doim"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat hozir"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat shu safar"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Planshet"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9f44841f573c..48f38583220d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Nhấn vào Menu để mở khóa."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vẽ hình để mở khóa"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Khẩn cấp"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Quay lại cuộc gọi"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Chính xác!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Thử lại"</string>
@@ -1125,8 +1126,8 @@
<string name="capital_off" msgid="7443704171014626777">"TẮT"</string>
<string name="checked" msgid="9179896827054513119">"đã chọn"</string>
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
- <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất tác vụ đang sử dụng"</string>
- <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất tác vụ bằng %1$s"</string>
+ <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
+ <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Mở bằng"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Mở bằng %1$s"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 32ac0cf297c4..bf381ec53751 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按 MENU 解锁。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"绘制解锁图案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼救"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通话"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正确!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"重试"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 82b079fd98af..a3b20d128726 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按選單鍵解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按選單鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖形以解除鎖定螢幕"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急電話"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 02c02008dea3..f6543c9285d2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按下 Menu 鍵解鎖。"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖案"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急撥號"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 7fe333d4bc33..85f54e9f9f5e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -832,7 +832,8 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Chofoza Menyu ukuvula."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dweba iphathini ukuvula"</string>
- <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Isimo esiphuthumayo"</string>
+ <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+ <skip />
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Buyela ekholini"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Lungile!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zama futhi"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb30c9be4eba..ac08d96ab303 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2173,6 +2173,29 @@
<attr name="required" />
</declare-styleable>
+ <!-- The <code>uses-native-library</code> specifies a native shared library that this
+ package requires to be linked against. Specifying this flag tells the
+ system to make the native library to be available to your app.
+
+ <p>On devices running R or lower, this is ignored and the app has access to all
+ the public native shared libraries that are exported from the platform. This is
+ also ignored if the app is targeting R or lower.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesNativeLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the library you use. -->
+ <attr name="name" />
+ <!-- Specify whether this native library is required for the application.
+ The default is true, meaning the application requires the
+ library, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ allow the application to be installed even if the library
+ doesn't exist, and you will need to check for its presence
+ dynamically at runtime. -->
+ <attr name="required" />
+ </declare-styleable>
+
<!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
library that this package requires to be statically linked against. Specifying
this tag tells the system to include this library's code in your class loader.
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index c6d6d1ff90d5..b50792ca6b38 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -28,7 +28,9 @@ public class PathTest extends TestCase {
final Path.FillType defaultFillType = path.getFillType();
final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
- assertFalse(fillType.equals(defaultFillType)); // Sanity check for the test itself.
+
+ // This test is only meaningful if it changes from the default.
+ assertFalse(fillType.equals(defaultFillType));
path.setFillType(fillType);
path.reset();
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index a5a2221e5532..ada8b000a26b 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -39,6 +39,7 @@
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.READ_DREAM_STATE"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2aa76ab..dd8f40d586bc 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
be able to connect to the internet when such a proxy is in use, since
all outgoing connections originate from this app. -->
<allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
- <!-- These are the packages that are white-listed to be able to run as system user -->
- <system-user-whitelisted-app package="com.android.settings" />
-
- <!-- These are the packages that shouldn't run as system user -->
- <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6798c0a3f87e..0286a7148b0b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -143,6 +143,9 @@ applications that come with the platform
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+
+ <!-- For permission hub 2 debugging only -->
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
<privapp-permissions package="com.android.phone">
diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl
new file mode 100644
index 000000000000..7672e22f8adc
--- /dev/null
+++ b/data/keyboards/Vendor_2e95_Product_7725.kl
@@ -0,0 +1,64 @@
+# 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.
+
+#
+# Scuf Vantage Controller
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# L2 Trigger axis
+axis 0x03 LTRIGGER
+# R2 Trigger axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index d8d96413a93d..43373ffbd3f4 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -409,10 +409,10 @@ public class Region implements Parcelable {
mNativeRegion = ni;
}
- /* add dummy parameter so constructor can be called from jni without
+ /* Add an unused parameter so constructor can be called from jni without
triggering 'not cloneable' exception */
@UnsupportedAppUsage
- private Region(long ni, int dummy) {
+ private Region(long ni, int unused) {
this(ni);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 6eba9acbab4e..3263f79888d6 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.os.Handler;
import android.os.RemoteException;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 083c2439aa87..418973204add 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,21 +28,16 @@ import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.wm.DisplayChangeController.OnDisplayChangingListener;
+import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
* frozen the screen, it will call into this class. This will then call all registered local
* controllers and give them a chance to queue up task changes to be applied synchronously with that
* rotation.
*/
-@Singleton
public class DisplayController {
private static final String TAG = "DisplayController";
@@ -55,7 +50,7 @@ public class DisplayController {
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
/**
- * Get's a display by id from DisplayManager.
+ * Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
@@ -169,10 +164,9 @@ public class DisplayController {
}
};
- @Inject
- public DisplayController(Context context, @Main Handler mainHandler,
+ public DisplayController(Context context, Handler handler,
IWindowManager wmService) {
- mHandler = mainHandler;
+ mHandler = handler;
mContext = context;
mWmService = wmService;
mChangeController = new DisplayChangeController(mHandler, mWmService);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 89f469a438a9..338ece5afbc2 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -30,6 +30,7 @@ import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -40,18 +41,12 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.view.IInputMethodManager;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.dagger.qualifiers.Main;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
*/
-@Singleton
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
@@ -66,21 +61,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private static final int DIRECTION_HIDE = 2;
private static final int FLOATING_IME_BOTTOM_INSET = -80;
- SystemWindows mSystemWindows;
- final Handler mHandler;
- final TransactionPool mTransactionPool;
-
- final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
-
- final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+ protected final IWindowManager mWmService;
+ protected final Handler mHandler;
+ private final TransactionPool mTransactionPool;
+ private final DisplayController mDisplayController;
+ private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+ private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
- @Inject
- public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- @Main Handler mainHandler, TransactionPool transactionPool) {
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Handler mainHandler, TransactionPool transactionPool) {
mHandler = mainHandler;
- mSystemWindows = syswin;
+ mWmService = wmService;
mTransactionPool = transactionPool;
- displayController.addDisplayWindowListener(this);
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
@@ -88,9 +82,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// Add's a system-ui window-manager specifically for ime. This type is special because
// WM will defer IME inset handling to it in multi-window scenarious.
PerDisplay pd = new PerDisplay(displayId,
- mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+ mDisplayController.getDisplayLayout(displayId).rotation());
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
@@ -103,7 +97,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (pd == null) {
return;
}
- if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+ if (mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
pd.startAnimation(true, false /* forceRestart */);
}
@@ -112,7 +106,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -270,7 +264,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
}
}
@@ -289,7 +283,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
// frame height so any reported frame that is <= nav-bar frame height is assumed to
// be floating.
- return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
.navBarFrameHeight();
}
@@ -304,9 +298,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
// This is a "floating" or "expanded" IME, so to get animations, just
// pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
- final int floatingInset = (int) (
- mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId).density()
- * FLOATING_IME_BOTTOM_INSET);
+ final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
+ .density() * FLOATING_IME_BOTTOM_INSET);
mImeFrame.bottom -= floatingInset;
} else if (newFrame.height() != 0) {
// Don't set a new frame if it's empty and hiding -- this maintains continuity
@@ -364,6 +357,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mAnimation.setInterpolator(INTERPOLATOR);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
+
@Override
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
@@ -386,10 +380,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.apply();
mTransactionPool.release(t);
}
+
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
+
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
@@ -449,18 +445,19 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
int IME_ANIMATION_NO_ALPHA = 1;
/** @hide */
- @IntDef(prefix = { "IME_ANIMATION_" }, value = {
+ @IntDef(prefix = {"IME_ANIMATION_"}, value = {
IME_ANIMATION_NO_ALPHA,
})
- @interface ImeAnimationFlags {}
+ @interface ImeAnimationFlags {
+ }
/**
* Called when the IME position is starting to animate.
*
- * @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param hiddenTop The y position of the top of the IME surface when it is hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
* @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
* @return flags that may alter how ime itself is animated (eg. no-alpha).
*/
@@ -476,8 +473,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*
* @param imeTop The current y position of the top of the IME surface.
*/
- default void onImePositionChanged(int displayId, int imeTop,
- SurfaceControl.Transaction t) {}
+ default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
+ }
/**
* Called when the IME position is done animating.
@@ -485,7 +482,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
* @param cancel {@code true} if this was cancelled. This implies another start is coming.
*/
default void onImeEndPositioning(int displayId, boolean cancel,
- SurfaceControl.Transaction t) {}
+ SurfaceControl.Transaction t) {
+ }
}
public IInputMethodManager getImms() {
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index a341f3050ea6..3181dbf74ace 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -52,7 +52,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* Contains information about the layout-properties of a display. This refers to internal layout
- * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
+ * like insets/cutout/rotation. In general, this can be thought of as the shell analog to
* DisplayPolicy.
*/
public class DisplayLayout {
@@ -345,9 +345,9 @@ public class DisplayLayout {
/** Retrieve the statusbar height from resources. */
static int getStatusBarHeight(boolean landscape, Resources res) {
return landscape ? res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_landscape)
- : res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_portrait);
+ com.android.internal.R.dimen.status_bar_height_landscape)
+ : res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
}
/** Calculate the DisplayCutout for a particular display size/rotation. */
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 21f67aef5604..8abe9eeb6a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
@@ -52,23 +52,18 @@ import com.android.internal.os.IResultReceiver;
import java.util.HashMap;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
- * Represents the "windowing" layer of the System-UI. This layer allows system-ui components to
- * place and manipulate windows without talking to WindowManager.
+ * Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and
+ * manipulate windows without talking to WindowManager.
*/
-@Singleton
public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
- final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
- Context mContext;
- IWindowSession mSession;
- DisplayController mDisplayController;
- IWindowManager mWmService;
+ private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
+ private final DisplayController mDisplayController;
+ private final IWindowManager mWmService;
+ private IWindowSession mSession;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -88,10 +83,7 @@ public class SystemWindows {
public void onDisplayRemoved(int displayId) { }
};
- @Inject
- public SystemWindows(Context context, DisplayController displayController,
- IWindowManager wmService) {
- mContext = context;
+ public SystemWindows(DisplayController displayController, IWindowManager wmService) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -172,7 +164,7 @@ public class SystemWindows {
/**
* Get the IWindow token for a specific root.
*
- * @param windowType A window type from {@link android.view.WindowManager}.
+ * @param windowType A window type from {@link WindowManager}.
*/
IWindow getWindow(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
@@ -215,8 +207,8 @@ public class SystemWindows {
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
- new SurfaceControlViewHost(mContext, display, wwm,
- true /* useSfChoreographer */);
+ new SurfaceControlViewHost(
+ view.getContext(), display, wwm, true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
@@ -318,7 +310,7 @@ public class SystemWindows {
}
}
- class ContainerWindow extends IWindow.Stub {
+ static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/TransactionPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
index 801cf8a7523b..4c34566b0d98 100644
--- a/packages/SystemUI/src/com/android/systemui/TransactionPool.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
@@ -14,24 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.wm.shell.common;
import android.util.Pools;
import android.view.SurfaceControl;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
*/
-@Singleton
public class TransactionPool {
private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
new Pools.SynchronizedPool<>(4);
- @Inject
- TransactionPool() {
+ public TransactionPool() {
}
/** Gets a transaction from the pool. */
diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp
index 78fa45ebdf94..9868879cebb9 100644
--- a/libs/WindowManager/Shell/tests/Android.bp
+++ b/libs/WindowManager/Shell/tests/Android.bp
@@ -36,9 +36,6 @@ android_test {
"libstaticjvmtiagent",
],
- sdk_version: "current",
- platform_apis: true,
-
optimize: {
enabled: false,
},
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
index 376875b143a1..f1ead3c8a441 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.tests;
+package com.android.wm.shell;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.WindowManagerShell;
-
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 9596a73eaf3e..2b5b77e49e3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
import static android.view.Surface.ROTATION_0;
@@ -35,12 +35,11 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.systemui.SysuiTestCase;
import org.junit.Test;
@SmallTest
-public class DisplayLayoutTest extends SysuiTestCase {
+public class DisplayLayoutTest {
@Test
public void testInsets() {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d25fc4b0b03e..b2c39c90071a 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -139,6 +139,9 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
(*mGlobalData)->reportJank();
}
+ if (mSwapDeadline < 0) {
+ mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval;
+ }
bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1);
mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4460266276f9..b3fbbfe98669 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,7 +75,7 @@ private:
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
- nsecs_t mSwapDeadline;
+ nsecs_t mSwapDeadline = -1;
// The amount of time we will erase from the total duration to account
// for SF vsync offsets with HWC2 blocking dequeueBuffers.
// (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index c0a24438987a..1a89cfd5d0ad 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -209,11 +209,8 @@ static SkImageInfo validateAlpha(const SkImageInfo& info) {
void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
mInfo = validateAlpha(newInfo);
- // Dirty hack is dirty
- // TODO: Figure something out here, Skia's current design makes this
- // really hard to work with. Skia really, really wants immutable objects,
- // but with the nested-ref-count hackery going on that's just not
- // feasible without going insane trying to figure it out
+ // TODO: Skia intends for SkPixelRef to be immutable, but this method
+ // modifies it. Find another way to support reusing the same pixel memory.
this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885cd1f37..997339eb2a80 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@ public final class GpsStatus {
return status;
}
+ /**
+ * Builds an empty GpsStatus. Should only be used for legacy reasons.
+ *
+ * @hide
+ */
+ @NonNull
+ static GpsStatus createEmpty() {
+ return new GpsStatus();
+ }
+
private GpsStatus() {
}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 6e7f6a52d669..29b483af8721 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -24,6 +24,6 @@ import android.os.IRemoteCallback;
*/
oneway interface ILocationListener
{
- void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+ void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
void onProviderEnabledChanged(String provider, boolean enabled);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bb8f81dfaa32..0e7eaa21888e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,7 +113,7 @@ interface ILocationManager
List<LocationRequest> getTestProviderCurrentRequests(String provider);
LocationTime getGnssTimeMillis();
- boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+ void sendExtraCommand(String provider, String command, inout Bundle extras);
// used by gts tests to verify whitelists
String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df08345c79b..2738ff4ff38c 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@ import android.os.Bundle;
public interface LocationListener {
/**
- * Called when the location has changed.
+ * Called when the location has changed. A wakelock is held on behalf on the listener for some
+ * brief amount of time as this callback executes. If this callback performs long running
+ * operations, it is the client's responsibility to obtain their own wakelock.
*
* @param location the updated location
*/
@@ -52,18 +54,17 @@ public interface LocationListener {
default void onStatusChanged(String provider, int status, Bundle extras) {}
/**
- * Called when the provider is enabled by the user.
+ * Called when a provider this listener is registered with becomes enabled.
*
- * @param provider the name of the location provider that has become enabled
+ * @param provider the name of the location provider
*/
default void onProviderEnabled(@NonNull String provider) {}
/**
- * Called when the provider is disabled by the user. If requestLocationUpdates
- * is called on an already disabled provider, this method is called
- * immediately.
+ * Called when the provider this listener is registered with becomes disabled. If a provider is
+ * disabled when this listener is registered, this callback will be invoked immediately.
*
- * @param provider the name of the location provider that has become disabled
+ * @param provider the name of the location provider
*/
default void onProviderDisabled(@NonNull String provider) {}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c0b8e1bf3bbe..1803027743f6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -303,7 +303,6 @@ public class LocationManager {
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
-
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
@GuardedBy("sLocationListeners")
@@ -311,7 +310,9 @@ public class LocationManager {
sLocationListeners = new WeakHashMap<>();
final Context mContext;
- @UnsupportedAppUsage
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "LocationManager}")
final ILocationManager mService;
private final Object mLock = new Object();
@@ -421,8 +422,7 @@ public class LocationManager {
try {
return mService.getExtraLocationControllerPackage();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -437,7 +437,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -452,7 +452,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -466,8 +466,7 @@ public class LocationManager {
try {
return mService.isExtraLocationControllerPackageEnabled();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -485,7 +484,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackage(packageName);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -503,7 +502,7 @@ public class LocationManager {
try {
mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -1199,7 +1198,7 @@ public class LocationManager {
mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(listener));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1301,7 +1300,7 @@ public class LocationManager {
// unregistration is complete.
mService.unregisterLocationListener(transport);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1517,7 +1516,8 @@ public class LocationManager {
Preconditions.checkArgument(command != null, "invalid null command");
try {
- return mService.sendExtraCommand(provider, command, extras);
+ mService.sendExtraCommand(provider, command, extras);
+ return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1835,6 +1835,10 @@ public class LocationManager {
} else {
status.setStatus(gnssStatus, ttff);
}
+ } else if (status == null) {
+ // even though this method is marked as nullable, legacy behavior was to never return
+ // a null result, and there are applications that rely on this behavior.
+ status = GpsStatus.createEmpty();
}
return status;
}
@@ -2424,7 +2428,7 @@ public class LocationManager {
try {
cancellationSignal.cancel();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -2464,7 +2468,8 @@ public class LocationManager {
}
@Override
- public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+ public void onLocationChanged(Location location,
+ @Nullable IRemoteCallback onCompleteCallback) {
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
@Override
public void operate(LocationListener listener) {
@@ -2473,7 +2478,13 @@ public class LocationManager {
@Override
public void onComplete(boolean success) {
- markComplete(onCompleteCallback);
+ if (onCompleteCallback != null) {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
});
}
@@ -2488,14 +2499,6 @@ public class LocationManager {
}
});
}
-
- private void markComplete(IRemoteCallback onCompleteCallback) {
- try {
- onCompleteCallback.sendResult(null);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
}
private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 00a4c7e19f34..6e3fb1991acc 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -18,7 +18,6 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.util.SparseIntArray;
import java.lang.annotation.Retention;
@@ -137,13 +136,15 @@ public final class AudioDeviceInfo {
*/
public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24;
/**
- * @hide
* A device type for rerouting audio within the Android framework between mixes and
- * system applications. Typically created when using
- * {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
- * {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_RENDER} flag.
- */
- @SystemApi
+ * system applications.
+ * This type is for instance encountered when querying the output device of a track
+ * (with {@link AudioTrack#getRoutedDevice()} playing from a device in screen mirroring mode,
+ * where the audio is not heard on the device, but on the remote device.
+ */
+ // Typically created when using
+ // {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
+ // {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_LOOP_BACK} flag.
public static final int TYPE_REMOTE_SUBMIX = 25;
/** @hide */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d690f0aa397..4a6724a09c1e 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1411,9 +1411,9 @@ public class ExifInterface {
private static final int IMAGE_TYPE_WEBP = 14;
static {
- sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US);
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX");
+ sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX", Locale.US);
sFormatterTz.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build up the hash tables to look up Exif tags for reading Exif tags.
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f93e30..029e61492b6d 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.media.MediaDescription;
-import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Bundle;
@@ -757,8 +756,8 @@ public final class MediaBrowser {
* Flag: Indicates that the item is playable.
* <p>
* The id of this item may be passed to
- * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
- * to start playing it.
+ * {@link android.media.session.MediaController.TransportControls
+ * #playFromMediaId(String, Bundle)} to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
@@ -1107,13 +1106,7 @@ public final class MediaBrowser {
}
@Override
- public void onLoadChildren(String parentId, ParceledListSlice list) {
- onLoadChildrenWithOptions(parentId, list, null);
- }
-
- @Override
- public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
- final Bundle options) {
+ public void onLoadChildren(String parentId, ParceledListSlice list, Bundle options) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
mediaBrowser.onLoadChildren(this, parentId, list, options);
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 7e3f2f8868fb..a8772076af97 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -23,7 +23,5 @@ oneway interface IMediaBrowserServiceCallbacks {
void onConnect(String root, in MediaSession.Token session, in Bundle extras);
@UnsupportedAppUsage
void onConnectFailed();
- void onLoadChildren(String mediaId, in ParceledListSlice list);
- void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list,
- in Bundle options);
+ void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 06adf30a8303..39c7682a2a74 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -687,7 +687,7 @@ public abstract class MediaBrowserService extends Service {
final ParceledListSlice<MediaBrowser.MediaItem> pls =
filteredList == null ? null : new ParceledListSlice<>(filteredList);
try {
- connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+ connection.callbacks.onLoadChildren(parentId, pls, options);
} catch (RemoteException ex) {
// The other side is in the process of crashing.
Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 0dc76313cf5c..2b95992b1edf 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -24061,6 +24061,7 @@ package android.media {
field public static final int TYPE_IP = 20; // 0x14
field public static final int TYPE_LINE_ANALOG = 5; // 0x5
field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+ field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
field public static final int TYPE_TELEPHONY = 18; // 0x12
field public static final int TYPE_TV_TUNER = 17; // 0x11
field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/non-updatable-api/removed.txt b/non-updatable-api/removed.txt
index ba05a1b89988..f2dfb84eb8fe 100644
--- a/non-updatable-api/removed.txt
+++ b/non-updatable-api/removed.txt
@@ -1,10 +1,6 @@
// Signature format: 2.0
package android.app {
- public class ActivityManager {
- method @Deprecated public static int getMaxNumPictureInPictureActions();
- }
-
public class Notification implements android.os.Parcelable {
method @Deprecated public String getChannel();
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index b41ab93295b5..222e563d4f96 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4112,10 +4112,6 @@ package android.media {
field public static final int ROLE_OUTPUT = 2; // 0x2
}
- public final class AudioDeviceInfo {
- field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
- }
-
public final class AudioFocusInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a294246dfaa..09fbf7a59a8c 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
deleted file mode 100644
index db71c91ca695..000000000000
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is the notification shade window. -->
-<com.android.systemui.statusbar.phone.NotificationShadeWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <com.android.systemui.statusbar.BackDropView
- android:id="@+id/backdrop"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- sysui:ignoreRightInset="true"
- >
- <ImageView android:id="@+id/backdrop_back"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"/>
- <ImageView android:id="@+id/backdrop_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:visibility="invisible"/>
- </com.android.systemui.statusbar.BackDropView>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_behind"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
- <include layout="@layout/brightness_mirror"/>
-
- <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout="@layout/car_fullscreen_user_switcher"/>
-
- <include layout="@layout/notification_center_activity"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginBottom="@dimen/navigation_bar_height"
- android:visibility="invisible"/>
-
- <include layout="@layout/status_bar_expanded"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible"/>
-
- <com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_in_front"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
-</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
deleted file mode 100644
index d93f62f8809d..000000000000
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
- <FrameLayout
- android:id="@+id/status_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
-
- <FrameLayout
- android:id="@+id/car_top_navigation_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c160c6..2c9788955bfa 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"
- android:layout_marginBottom="@dimen/navigation_bar_height"/>
+ android:layout="@layout/notification_panel_container"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index cb321cdc6c4d..8359dac6a30f 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -81,6 +81,21 @@
<dimen name="car_keyline_2">96dp</dimen>
<dimen name="car_keyline_3">128dp</dimen>
+ <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
+ <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+ <!-- Margin between text lines in Ongoing App Ops dialog -->
+ <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+ <!-- Padding around Ongoing App Ops dialog content -->
+ <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+ <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+ <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+ <!-- Start and End padding for Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+
<!-- Car volume dimens. -->
<dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index ffdf378959c9..797a178c9a4b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -21,8 +21,6 @@ import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.car.navigationbar.CarNavigationBar;
import com.android.systemui.car.notification.CarNotificationModule;
import com.android.systemui.car.sideloaded.SideLoadedAppController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarModule;
import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
import com.android.systemui.car.volume.VolumeUI;
import com.android.systemui.car.window.OverlayWindowModule;
@@ -37,10 +35,10 @@ import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -51,7 +49,7 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+@Module(includes = {RecentsModule.class, StatusBarModule.class, NotificationsModule.class,
BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class,
CarNotificationModule.class})
public abstract class CarSystemUIBinder {
@@ -162,19 +160,7 @@ public abstract class CarSystemUIBinder {
@Binds
@IntoMap
@ClassKey(StatusBar.class)
- public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
-
- /** Inject into TvStatusBar. */
- @Binds
- @IntoMap
- @ClassKey(TvStatusBar.class)
- public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
-
- /** Inject into StatusBarGoogle. */
- @Binds
- @IntoMap
- @ClassKey(CarStatusBar.class)
- public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+ public abstract SystemUI bindsStatusBar(StatusBar sysui);
/** Inject into VolumeUI. */
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 496742680893..7b6dceb5fcd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -22,14 +22,13 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
+import android.view.IWindowManager;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarKeyguardViewManager;
import com.android.systemui.car.statusbar.DozeServiceHost;
import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
import com.android.systemui.car.volume.CarVolumeDialogComponent;
@@ -59,16 +58,17 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.DisplaySystemBarsController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -99,10 +99,6 @@ public abstract class CarSystemUIModule {
groupManager, configurationController);
}
- @Binds
- abstract DisplayImeController bindDisplayImeController(
- DisplaySystemBarsController displaySystemBarsController);
-
@Singleton
@Provides
@Named(LEAK_REPORT_EMAIL_NAME)
@@ -117,6 +113,31 @@ public abstract class CarSystemUIModule {
return new Recents(context, recentsImplementation, commandQueue);
}
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController providerDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Binds
+ abstract DisplayImeController bindDisplayImeController(
+ DisplaySystemBarsController displaySystemBarsController);
+
@Binds
abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
@@ -158,17 +179,10 @@ public abstract class CarSystemUIModule {
CarSystemUIRootComponent systemUIRootComponent);
@Binds
- public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
-
- @Binds
abstract VolumeDialogComponent bindVolumeDialogComponent(
CarVolumeDialogComponent carVolumeDialogComponent);
@Binds
- abstract StatusBarKeyguardViewManager bindStatusBarKeyguardViewManager(
- CarStatusBarKeyguardViewManager keyguardViewManager);
-
- @Binds
abstract KeyguardViewController bindKeyguardViewController(
CarKeyguardViewController carKeyguardViewController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc6c0d0..51a7245ea5c6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
public void onFinishInflate() {
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1eead62c042a..8d5843635e5f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
package com.android.systemui.car.notification;
+import static android.view.WindowInsets.Type.navigationBars;
+
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
@Override
+ protected boolean shouldShowStatusBar() {
+ return true;
+ }
+
+ @Override
+ protected int getInsetTypesToFit() {
+ return navigationBars();
+ }
+
+ @Override
protected boolean shouldShowHUN() {
return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
deleted file mode 100644
index d692487d410e..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarDeviceProvisionedListener;
-import com.android.systemui.car.bluetooth.CarBatteryController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.classifier.FalsingLog;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import dagger.Lazy;
-
-/**
- * A status bar tailored for the automotive use case.
- */
-public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
- private static final String TAG = "CarStatusBar";
-
- private final UserSwitcherController mUserSwitcherController;
- private final ScrimController mScrimController;
-
- private CarBatteryController mCarBatteryController;
- private BatteryMeterView mBatteryMeterView;
- private Drawable mNotificationPanelBackground;
-
- private final Object mQueueLock = new Object();
- private final CarNavigationBarController mCarNavigationBarController;
- private final CarDeviceProvisionedController mCarDeviceProvisionedController;
- private final ScreenLifecycle mScreenLifecycle;
-
- private boolean mDeviceIsSetUpForUser = true;
- private boolean mIsUserSetupInProgress = false;
-
- public CarStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recents,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> depthControllerLazy,
- /* Car Settings injected components. */
- CarNavigationBarController carNavigationBarController) {
- super(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- null /* keyguardLiftController */,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recents,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- superStatusBarViewFactory,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- depthControllerLazy,
- statusBarTouchableRegionManager);
- mUserSwitcherController = userSwitcherController;
- mScrimController = scrimController;
- mCarDeviceProvisionedController = carDeviceProvisionedController;
- mCarNavigationBarController = carNavigationBarController;
- mScreenLifecycle = screenLifecycle;
- }
-
- @Override
- public void start() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
-
- super.start();
-
- createBatteryController();
- mCarBatteryController.startListening();
-
- mCarDeviceProvisionedController.addCallback(
- new CarDeviceProvisionedListener() {
- @Override
- public void onUserSetupInProgressChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSetupChanged() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
-
- @Override
- public void onUserSwitched() {
- mDeviceIsSetUpForUser = mCarDeviceProvisionedController
- .isCurrentUserSetup();
- mIsUserSetupInProgress = mCarDeviceProvisionedController
- .isCurrentUserSetupInProgress();
- }
- });
-
- mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return TAG;
- }
-
- @Override
- public boolean suppressInterruptions(NotificationEntry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded.
- // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
- // the shade is already opened.
- return !getPresenter().isPresenterFullyCollapsed();
- }
- });
- }
-
- @Override
- public boolean hideKeyguard() {
- boolean result = super.hideKeyguard();
- mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
- return result;
- }
-
- @Override
- public void showKeyguard() {
- super.showKeyguard();
- mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
- }
-
- private boolean isDeviceSetupForUser() {
- return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
- }
-
- @Override
- protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
- super.makeStatusBarView(result);
-
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
-
- FragmentHostManager manager = FragmentHostManager.get(mPhoneStatusBarWindow);
- manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
- mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
-
- // By default, the BatteryMeterView should not be visible. It will be toggled
- // when a device has connected by bluetooth.
- mBatteryMeterView.setVisibility(View.GONE);
- });
- }
-
- @Override
- public void animateExpandNotificationsPanel() {
- // No op.
- }
-
- @Override
- protected QS createDefaultQSFragment() {
- return null;
- }
-
- private BatteryController createBatteryController() {
- mCarBatteryController = new CarBatteryController(mContext);
- mCarBatteryController.addBatteryViewHandler(this);
- return mCarBatteryController;
- }
-
- @Override
- protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- // No op.
- }
-
- @Override
- public void notifyBiometricAuthModeChanged() {
- // No op.
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- //When executing dump() function simultaneously, we need to serialize them
- //to get mStackScroller's position correctly.
- synchronized (mQueueLock) {
- pw.println(" mStackScroller: " + viewInfo(mStackScroller));
- pw.println(" mStackScroller: " + viewInfo(mStackScroller)
- + " scroll " + mStackScroller.getScrollX()
- + "," + mStackScroller.getScrollY());
- }
- pw.print(" mCarBatteryController=");
- pw.println(mCarBatteryController);
- pw.print(" mBatteryMeterView=");
- pw.println(mBatteryMeterView);
-
- if (Dependency.get(KeyguardUpdateMonitor.class) != null) {
- Dependency.get(KeyguardUpdateMonitor.class).dump(fd, pw, args);
- }
-
- FalsingLog.dump(pw);
-
- pw.println("SharedPreferences:");
- for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
- pw.print(" ");
- pw.print(entry.getKey());
- pw.print("=");
- pw.println(entry.getValue());
- }
- }
-
- @Override
- public void showBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void hideBatteryView() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
- }
-
- if (mBatteryMeterView != null) {
- mBatteryMeterView.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void createUserSwitcher() {
- if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
- super.createUserSwitcher();
- }
- }
-
- /**
- * Dismisses the keyguard and shows bouncer if authentication is necessary.
- */
- public void dismissKeyguard() {
- // Don't dismiss keyguard when the screen is off.
- if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
- return;
- }
- executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
- }
-
- /**
- * Ensures that relevant child views are appropriately recreated when the device's density
- * changes.
- */
- @Override
- public void onDensityOrFontScaleChanged() {
- super.onDensityOrFontScaleChanged();
- // Need to update the background on density changed in case the change was due to night
- // mode.
- mNotificationPanelBackground = getDefaultWallpaper();
- mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
- }
-
- /**
- * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
- */
- private Drawable getDefaultWallpaper() {
- return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
deleted file mode 100644
index 96a998a500e1..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.R;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Car implementation of the {@link StatusBarKeyguardViewManager}. */
-@Singleton
-public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
-
- protected boolean mShouldHideNavBar;
- private final CarNavigationBarController mCarNavigationBarController;
- private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet;
-
- @Inject
- public CarStatusBarKeyguardViewManager(Context context,
- ViewMediatorCallback callback,
- LockPatternUtils lockPatternUtils,
- SysuiStatusBarStateController sysuiStatusBarStateController,
- ConfigurationController configurationController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- NavigationModeController navigationModeController,
- DockManager dockManager,
- NotificationShadeWindowController notificationShadeWindowController,
- KeyguardStateController keyguardStateController,
- NotificationMediaManager notificationMediaManager,
- CarNavigationBarController carNavigationBarController) {
- super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
- configurationController, keyguardUpdateMonitor, navigationModeController,
- dockManager, notificationShadeWindowController, keyguardStateController,
- notificationMediaManager);
- mShouldHideNavBar = context.getResources()
- .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
- mCarNavigationBarController = carNavigationBarController;
- mKeygaurdCancelClickedListenerSet = new HashSet<>();
- }
-
- @Override
- protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if (!mShouldHideNavBar) {
- return;
- }
- int visibility = navBarVisible ? View.VISIBLE : View.GONE;
- mCarNavigationBarController.setBottomWindowVisibility(visibility);
- mCarNavigationBarController.setLeftWindowVisibility(visibility);
- mCarNavigationBarController.setRightWindowVisibility(visibility);
- }
-
- /**
- * Car is a multi-user system. There's a cancel button on the bouncer that allows the user to
- * go back to the user switcher and select another user. Different user may have different
- * security mode which requires bouncer container to be resized. For this reason, the bouncer
- * view is destroyed on cancel.
- */
- @Override
- protected boolean shouldDestroyViewOnReset() {
- return true;
- }
-
- /**
- * Called when cancel button in bouncer is pressed.
- */
- @Override
- public void onCancelClicked() {
- mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked);
- }
-
- /**
- * Do nothing on this change.
- * The base class hides the keyguard which for automotive we want to avoid b/c this would happen
- * on a configuration change due to day/night (headlight state).
- */
- @Override
- public void onDensityOrFontScaleChanged() { }
-
- /**
- * Add listener for keyguard cancel clicked.
- */
- public void addOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener);
- }
-
- /**
- * Remove listener for keyguard cancel clicked.
- */
- public void removeOnKeyguardCancelClickedListener(
- OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
- mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener);
- }
-
-
- /**
- * Defines a callback for keyguard cancel button clicked listeners.
- */
- public interface OnKeyguardCancelClickedListener {
- /**
- * Called when keyguard cancel button is clicked.
- */
- void onCancelClicked();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
deleted file mode 100644
index dc2eb04c2990..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.InitController;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing {@link CarStatusBar}.
- */
-@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
- NotificationRowModule.class})
-public class CarStatusBarModule {
- /**
- * Provides our instance of StatusBar which is considered optional.
- */
- @Provides
- @Singleton
- static CarStatusBar provideStatusBar(
- Context context,
- NotificationsController notificationsController,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- BypassHeadsUpNotifier bypassHeadsUpNotifier,
- FalsingManager falsingManager,
- BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptionStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
- BubbleController bubbleController,
- NotificationGroupManager groupManager,
- VisualStabilityManager visualStabilityManager,
- CarDeviceProvisionedController carDeviceProvisionedController,
- NavigationBarController navigationBarController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- LockscreenLockIconController lockscreenLockIconController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
- Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
- PluginManager pluginManager,
- Optional<Divider> dividerOptional,
- SuperStatusBarViewFactory superStatusBarViewFactory,
- LightsOutNotifController lightsOutNotifController,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- DarkIconDispatcher darkIconDispatcher,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
- CarNavigationBarController carNavigationBarController) {
- return new CarStatusBar(
- context,
- notificationsController,
- lightBarController,
- autoHideController,
- keyguardUpdateMonitor,
- statusBarIconController,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- bypassHeadsUpNotifier,
- falsingManager,
- broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptionStateProvider,
- notificationViewHierarchyManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- vibratorHelper,
- bubbleController,
- groupManager,
- visualStabilityManager,
- carDeviceProvisionedController,
- navigationBarController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- lockscreenLockIconController,
- dozeParameters,
- scrimController,
- lockscreenWallpaperLazy,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- recentsOptional,
- statusBarComponentBuilder,
- pluginManager,
- dividerOptional,
- superStatusBarViewFactory,
- lightsOutNotifController,
- statusBarNotificationActivityStarterBuilder,
- shadeController,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- darkIconDispatcher,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- dismissCallbackRegistry,
- statusBarTouchableRegionManager,
- notificationShadeDepthControllerLazy,
- carNavigationBarController);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d342fb6e..0d77c1341ffb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
+ @Override
+ protected int getInsetTypesToFit() {
+ return 0;
+ }
+
/**
* Makes the user switch transition view appear and draws the content inside of it if a user
* that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92c690a..53deb9d9dc5d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.statusBars;
+
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
/**
* Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@ public class OverlayViewController {
}
/**
+ * Returns {@code true} if status bar should be displayed over this view.
+ */
+ protected boolean shouldShowStatusBar() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if this view should be hidden during the occluded state.
*/
protected boolean shouldShowWhenOccluded() {
return false;
}
+
+ /**
+ * Returns the insets types to fit to the sysui overlay window when this
+ * {@link OverlayViewController} is in the foreground.
+ */
+ @WindowInsets.Type.InsetsType
+ protected int getInsetTypesToFit() {
+ return statusBars();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e9410964313..2494242c24f0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import android.annotation.Nullable;
import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -48,10 +52,7 @@ public class OverlayViewGlobalStateController {
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- private final CarNavigationBarController mCarNavigationBarController;
-
- private boolean mIsOccluded;
-
+ private final WindowInsetsController mWindowInsetsController;
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
@@ -60,14 +61,15 @@ public class OverlayViewGlobalStateController {
Set<OverlayViewController> mViewsHiddenForOcclusion;
@VisibleForTesting
OverlayViewController mHighestZOrder;
+ private boolean mIsOccluded;
@Inject
public OverlayViewGlobalStateController(
- CarNavigationBarController carNavigationBarController,
SystemUIOverlayWindowController systemUIOverlayWindowController) {
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
- mCarNavigationBarController = carNavigationBarController;
+ mWindowInsetsController =
+ mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@ public class OverlayViewGlobalStateController {
}
updateInternalsWhenShowingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
debugLog();
@@ -185,7 +189,9 @@ public class OverlayViewGlobalStateController {
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
+ refreshInsetTypesToFit();
refreshNavigationBarVisibility();
+ refreshStatusBarVisibility();
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
@@ -208,10 +214,28 @@ public class OverlayViewGlobalStateController {
}
private void refreshNavigationBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
- mCarNavigationBarController.showBars();
+ mWindowInsetsController.show(navigationBars());
} else {
- mCarNavigationBarController.hideBars();
+ mWindowInsetsController.hide(navigationBars());
+ }
+ }
+
+ private void refreshStatusBarVisibility() {
+ mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+ mWindowInsetsController.show(statusBars());
+ } else {
+ mWindowInsetsController.hide(statusBars());
+ }
+ }
+
+ private void refreshInsetTypesToFit() {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ setFitInsetsTypes(statusBars());
+ } else {
+ setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
}
}
@@ -224,6 +248,10 @@ public class OverlayViewGlobalStateController {
mSystemUIOverlayWindowController.setWindowVisible(visible);
}
+ private void setFitInsetsTypes(@InsetsType int types) {
+ mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+ }
+
/**
* Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
* sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f63a2b4..029bd3702afe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.systemui.R;
@@ -99,7 +100,6 @@ public class SystemUIOverlayWindowController implements
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(/* types= */ 0);
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("SystemUIOverlayWindow");
mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@ public class SystemUIOverlayWindowController implements
setWindowVisible(false);
}
+ /** Sets the types of insets to fit. Note: This should be rarely used. */
+ public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+ mLpChanged.setFitInsetsTypes(types);
+ updateWindow();
+ }
+
/** Sets the window to the visible state. */
public void setWindowVisible(boolean visible) {
mVisible = visible;
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index a831464e7987..5c80202ba592 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -16,12 +16,14 @@
package com.android.systemui.wm;
+import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -29,8 +31,10 @@ import android.view.WindowInsets;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.TransactionPool;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -47,29 +51,32 @@ public class DisplaySystemBarsController extends DisplayImeController {
private static final String TAG = "DisplaySystemBarsController";
private SparseArray<PerDisplay> mPerDisplaySparseArray;
+ private final Context mContext;
@Inject
public DisplaySystemBarsController(
- SystemWindows syswin,
+ Context context,
+ IWindowManager wmService,
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
- super(syswin, displayController, mainHandler, transactionPool);
+ super(wmService, displayController, mainHandler, transactionPool);
+ mContext = context;
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ mWmService.setDisplayWindowInsetsController(displayId, pd);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
}
// Lazy loading policy control filters instead of during boot.
if (mPerDisplaySparseArray == null) {
mPerDisplaySparseArray = new SparseArray<>();
- BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
- BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+ BarControlPolicy.reloadFromSetting(mContext);
+ BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
int size = mPerDisplaySparseArray.size();
for (int i = 0; i < size; i++) {
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
@@ -82,7 +89,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
@Override
public void onDisplayRemoved(int displayId) {
try {
- mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
}
@@ -152,7 +159,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
showInsets(barVisibilities[0], /* fromIme= */ false);
hideInsets(barVisibilities[1], /* fromIme= */ false);
try {
- mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to update window manager service.");
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 20f9bc8ec1cb..ff286650ea50 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,19 +33,18 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@@ -58,8 +62,6 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private ViewGroup mBaseLayout;
@Mock
- private CarNavigationBarController mCarNavigationBarController;
- @Mock
private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
@Mock
private OverlayViewMediator mOverlayViewMediator;
@@ -71,18 +73,22 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+ mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+ when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
+ mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
}
@@ -108,7 +114,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -118,7 +124,37 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -168,10 +204,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -183,7 +220,46 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -216,10 +292,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -231,7 +308,44 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -402,10 +516,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -418,7 +533,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -441,10 +597,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
setupOverlayViewController2();
setOverlayViewControllerAsShowing(mOverlayViewController2);
when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).hideBars();
+ verify(mWindowInsetsController).hide(navigationBars());
}
@Test
@@ -457,7 +614,48 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ reset(mWindowInsetsController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).hide(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+ when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
}
@Test
@@ -479,7 +677,27 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController).showBars();
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_statusBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
}
@Test
@@ -615,7 +833,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
- Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ reset(mSystemUIOverlayWindowController);
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
index 29cc8eec4bc3..391f75e35382 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -33,7 +33,8 @@ import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
@@ -51,8 +52,6 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
private static final int DISPLAY_ID = 1;
@Mock
- private SystemWindows mSystemWindows;
- @Mock
private IWindowManager mIWindowManager;
@Mock
private DisplayController mDisplayController;
@@ -64,11 +63,10 @@ public class DisplaySystemBarsControllerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSystemWindows.mContext = mContext;
- mSystemWindows.mWmService = mIWindowManager;
mController = new DisplaySystemBarsController(
- mSystemWindows,
+ mContext,
+ mIWindowManager,
mDisplayController,
mHandler,
mTransactionPool
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f1..02f4457bffdb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@ import android.text.Html;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -75,6 +76,14 @@ public class DeviceChooserActivity extends Activity {
mDeviceListView = findViewById(R.id.device_list);
final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
mDeviceListView.setAdapter(adapter);
+ mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+ getService().mSelectedDevice =
+ (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+ adapter.notifyDataSetChanged();
+ }
+ });
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
@@ -157,4 +166,4 @@ public class DeviceChooserActivity extends Activity {
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-} \ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e39307..bcaee367b03c 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@ public class DeviceDiscoveryService extends Service {
? WIFI_ICON
: BLUETOOTH_ICON,
null, null, null);
- textView.setOnClickListener((view) -> {
- mSelectedDevice = device;
- notifyDataSetChanged();
- });
}
//TODO move to a layout file
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d480ff63d8e3..508cbfccffe9 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -140,8 +140,8 @@
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
- <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplicações removidas"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplicações e utilizadores removidos"</string>
+ <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Apps removidas"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Apps e utilizadores removidos"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Atualizações do sistema"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"Ligação USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
@@ -365,7 +365,7 @@
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
<string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
- <string name="debug_applications_category" msgid="5394089406638954196">"Aplicações"</string>
+ <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
<string name="app_process_limit_title" msgid="8361367869453043007">"Limite do processo em 2º plano"</string>
@@ -396,7 +396,7 @@
<item msgid="4548987861791236754">"Cores naturais e realistas"</item>
<item msgid="1282170165150762976">"Cores otimizadas para conteúdos digitais"</item>
</string-array>
- <string name="inactive_apps_title" msgid="5372523625297212320">"Aplicações em espera"</string>
+ <string name="inactive_apps_title" msgid="5372523625297212320">"Apps em espera"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
<string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 6269a717b333..fd986e5d13fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -90,7 +90,7 @@ public class AccessPointPreference extends Preference {
return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
}
- // Used for dummy pref.
+ // Used for fake pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mFrictionSld = null;
@@ -142,7 +142,7 @@ public class AccessPointPreference extends Preference {
public void onBindViewHolder(final PreferenceViewHolder view) {
super.onBindViewHolder(view);
if (mAccessPoint == null) {
- // Used for dummy pref.
+ // Used for fake pref.
return;
}
Drawable drawable = getIcon();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3015397ff1a3..bf5ab1c9951a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -256,7 +256,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
/**
- * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * Validity warning: this wipes out mScoreCache, so use with extreme caution
* @param workThread substitute Handler thread, for testing purposes only
*/
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 0e6a60bf47c1..1ace0b4250b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -118,7 +118,7 @@ public class WifiUtils {
final int maxDisplayedScans = 4;
int num5 = 0; // number of scanned BSSID on 5GHz band
int num24 = 0; // number of scanned BSSID on 2.4Ghz band
- int numBlackListed = 0;
+ int numBlockListed = 0;
// TODO: sort list by RSSI or age
long nowMs = SystemClock.elapsedRealtime();
@@ -170,8 +170,8 @@ public class WifiUtils {
}
visibility.append(scans5GHz.toString());
}
- if (numBlackListed > 0) {
- visibility.append("!").append(numBlackListed);
+ if (numBlockListed > 0) {
+ visibility.append("!").append(numBlockListed);
}
visibility.append("]");
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7f7afcbf11f5..8253c5e642f3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -214,6 +214,9 @@
<!-- Permission needed to test tcp keepalive offload. -->
<uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+ <!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
+ <uses-permission android:name="android.permission.RESET_APP_ERRORS" />
+
<!-- Permission needed to run keyguard manager tests in CTS -->
<uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index dfc47587b91c..2fbd9ba05817 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -45,7 +45,7 @@ android_library {
"WindowManager-Shell",
"SystemUIPluginLib",
"SystemUISharedLib",
- "SystemUI-statsd",
+ "SystemUI-statsd",
"SettingsLib",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6aa233b2f292..98d3553287d1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -239,6 +239,7 @@
<!-- Listen app op changes -->
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
@@ -395,19 +396,15 @@
<!-- Springboard for launching the share and edit activity. This needs to be in the main
system ui process since we need to notify the status bar to dismiss the keyguard -->
- <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
- android:exported="false" />
-
- <!-- Callback for dismissing screenshot notification after a share target is picked -->
- <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+ <receiver android:name=".screenshot.ActionProxyReceiver"
android:exported="false" />
<!-- Callback for deleting screenshot notification -->
- <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
+ <receiver android:name=".screenshot.DeleteScreenshotReceiver"
android:exported="false" />
<!-- Callback for invoking a smart action from the screenshot notification. -->
- <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+ <receiver android:name=".screenshot.SmartActionsReceiver"
android:exported="false"/>
<!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
new file mode 100644
index 000000000000..827cf4a9d3b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#242424" /> <!-- 14% of white -->
+ <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
+ android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
+ <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e4f0ed..213bb923db65 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -87,4 +87,4 @@
/>
</LinearLayout>
</LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
new file mode 100644
index 000000000000..3c306322d21f
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+
+<com.android.systemui.privacy.OngoingPrivacyChip
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/privacy_chip"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ android:focusable="true" >
+
+ <FrameLayout
+ android:id="@+id/background"
+ android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_gravity="center_vertical">
+ <LinearLayout
+ android:id="@+id/icons_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ />
+ </FrameLayout>
+</com.android.systemui.privacy.OngoingPrivacyChip> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index be86e5f5abc5..3c7480181877 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -14,7 +14,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_status_bar_system_icons"
@@ -27,6 +27,13 @@
android:clickable="true"
android:paddingTop="@dimen/status_bar_padding_top" >
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start" >
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -38,5 +45,23 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
systemui:showDark="false" />
+ </LinearLayout>
+
+ <android.widget.Space
+ android:id="@+id/space"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|end" >
+
+ <include layout="@layout/ongoing_privacy_chip" />
-</FrameLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 8e10230f2212..27bc3ab4aab0 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1021,9 +1021,9 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғы басқару виджеттері"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
- <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғы басқару виджеттерін реттеу"</string>
+ <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғыны басқару элементтерін реттеу"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Басқару элементтерін шығару үшін қуат түймесін басып тұрыңыз."</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері енгізілетін қолданбаны таңдаңыз"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
@@ -1046,7 +1046,7 @@
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Басқару элементтері жүктелмеді. Қолданба параметрлерінің өзгермегенін тексеру үшін <xliff:g id="APP">%s</xliff:g> қолданбасын қараңыз."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Үйлесімді басқару элементтері қолжетімді емес."</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Басқа"</string>
- <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару виджеттеріне қосу"</string>
+ <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару элементтеріне қосу"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Енгізу"</string>
<string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ұсынған"</string>
<string name="controls_dialog_confirmation" msgid="586517302736263447">"Басқару элементтері жаңартылды"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6b8b674ea412..341efff0e4cd 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -811,7 +811,7 @@
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificações"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atalhos de teclado"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar esquema de teclado"</string>
- <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplicações"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Apps"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Assistência"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Contactos"</string>
@@ -965,7 +965,7 @@
<string name="qs_dnd_until" msgid="7844269319043747955">"Até à(s) <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="qs_dnd_keep" msgid="3829697305432866434">"Manter"</string>
<string name="qs_dnd_replace" msgid="7712119051407052689">"Substituir"</string>
- <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplicações em execução em segundo plano"</string>
+ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Pretende desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 27db8cb5fb46..f407a8dcc57f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -499,6 +499,8 @@
<item>com.android.systemui</item>
</string-array>
+ <integer name="ongoing_appops_dialog_max_apps">5</integer>
+
<!-- Launcher package name for overlaying icons. -->
<string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ea855eb722b9..122fcb21a9f4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1137,6 +1137,23 @@
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
+
+ <!-- Height of the Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_height">32dp</dimen>
+ <!-- Padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
+ <!-- Side padding between background of Ongoing App Ops chip and content -->
+ <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+ <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip when QS-->
+ <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+ <!-- Icon size of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
+ <!-- Radius of Ongoing App Ops chip corners -->
+ <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+
+
<!-- How much each bubble is elevated. -->
<dimen name="bubble_elevation">1dp</dimen>
<!-- How much the bubble flyout text container is elevated. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d97aea7dab59..8b6543ac73bd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2596,6 +2596,27 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
+ <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
+
+ <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
+ <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+
+ <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_separator">,\u0020</string>
+
+ <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+ <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
+
+ <!-- Text for camera app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_camera">camera</string>
+
+ <!-- Text for location app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_location">location</string>
+
+ <!-- Text for microphone app op [CHAR LIMIT=20]-->
+ <string name="privacy_type_microphone">microphone</string>
+
<!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
<string name="sensor_privacy_mode">Sensors off</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8a36e7b127db..878947f6ba37 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -287,11 +287,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Executor mBackgroundExecutor;
/**
- * Short delay before restarting biometric authentication after a successful try
- * This should be slightly longer than the time between on<biometric>Authenticated
- * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
+ * Short delay before restarting fingerprint authentication after a successful try. This should
+ * be slightly longer than the time between onFingerprintAuthenticated and
+ * setKeyguardGoingAway(true).
*/
- private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
+ private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
// If the HAL dies or is unable to authenticate, keyguard should retry after a short delay
private int mHardwareFingerprintUnavailableRetryCount = 0;
@@ -599,7 +599,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
+ FINGERPRINT_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
@@ -782,9 +782,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
- BIOMETRIC_CONTINUE_DELAY_MS);
-
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 02d2b8e4ef0f..4dbb92e1e37f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -122,9 +123,9 @@ import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
import java.util.function.Consumer;
@@ -294,6 +295,7 @@ public class Dependency {
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+ @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
@Inject @Background Lazy<Handler> mBgHandler;
@Inject @Main Lazy<Looper> mMainLooper;
@@ -491,6 +493,7 @@ public class Dependency {
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
+ mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 000000000000..769a344eedac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+ private final SparseArray<T> mSparseArray = new SparseArray<>();
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * @param displayManager DisplayManager
+ */
+ DisplayIdIndexSupplier(DisplayManager displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ /**
+ * @param displayId the logical display Id
+ * @return {@code null} if the given display id is invalid
+ */
+ @Nullable
+ public T get(int displayId) {
+ T instance = mSparseArray.get(displayId);
+ if (instance != null) {
+ return instance;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return null;
+ }
+ instance = createInstance(display);
+ mSparseArray.put(displayId, instance);
+ return instance;
+ }
+
+ @NonNull
+ protected abstract T createInstance(Display display);
+
+ /**
+ * Removes the instance with given display Id.
+ *
+ * @param displayId the logical display id
+ */
+ public void remove(int displayId) {
+ mSparseArray.remove(displayId);
+ }
+
+ /**
+ * Clears all elements.
+ */
+ public void clear() {
+ mSparseArray.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 398a2c9c9d41..68a0a65ef50f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
*/
class MagnificationModeSwitch {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13ceac1..ffc70bcf63d0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@ package com.android.systemui.accessibility;
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+
import javax.inject.Singleton;
/**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ * <li> Both full-screen and window magnification mode are capable.</li>
+ * <li> The magnification scale is changed by a user.</li>
+ * <ol>
*/
@Singleton
public class ModeSwitchesController {
- private static final String TAG = "ModeSwitchesController";
-
- private final Context mContext;
- private final DisplayManager mDisplayManager;
-
- private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
- new SparseArray<>();
+ private final SwitchSupplier mSwitchSupplier;
public ModeSwitchesController(Context context) {
- mContext = context;
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mSwitchSupplier = new SwitchSupplier(context,
+ context.getSystemService(DisplayManager.class));
+ }
+
+ @VisibleForTesting
+ ModeSwitchesController(SwitchSupplier switchSupplier) {
+ mSwitchSupplier = switchSupplier;
}
/**
@@ -52,20 +54,17 @@ public class ModeSwitchesController {
*
* @param displayId The logical display id
* @param mode The magnification mode
- *
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
*/
@MainThread
void showButton(int displayId, int mode) {
- if (mDisplaysToSwitches.get(displayId) == null) {
- final MagnificationModeSwitch magnificationModeSwitch =
- createMagnificationSwitchController(displayId);
- if (magnificationModeSwitch == null) {
- return;
- }
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
+ return;
}
- mDisplaysToSwitches.get(displayId).showButton(mode);
+ magnificationModeSwitch.showButton(mode);
}
/**
@@ -74,30 +73,34 @@ public class ModeSwitchesController {
* @param displayId The logical display id
*/
void removeButton(int displayId) {
- if (mDisplaysToSwitches.get(displayId) == null) {
+ final MagnificationModeSwitch magnificationModeSwitch =
+ mSwitchSupplier.get(displayId);
+ if (magnificationModeSwitch == null) {
return;
}
- mDisplaysToSwitches.get(displayId).removeButton();
+ magnificationModeSwitch.removeButton();
}
- private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
- if (mDisplayManager.getDisplay(displayId) == null) {
- Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
- return null;
+ @VisibleForTesting
+ static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+ private final Context mContext;
+
+ /**
+ * @param context Context
+ * @param displayManager DisplayManager
+ */
+ SwitchSupplier(Context context, DisplayManager displayManager) {
+ super(displayManager);
+ mContext = context;
}
- final MagnificationModeSwitch
- magnificationModeSwitch = new MagnificationModeSwitch(
- getDisplayContext(displayId));
- mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
- return magnificationModeSwitch;
- }
- private Context getDisplayContext(int displayId) {
- final Display display = mDisplayManager.getDisplay(displayId);
- final Context context = (displayId == Display.DEFAULT_DISPLAY)
- ? mContext
- : mContext.createDisplayContext(display);
- return context;
+ @Override
+ protected MagnificationModeSwitch createInstance(Display display) {
+ final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+ ? mContext
+ : mContext.createDisplayContext(display);
+ return new MagnificationModeSwitch(context);
+ }
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 5fd7b53435cf..4df66602bb7e 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.appops;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -25,11 +26,14 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.WorkerThread;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,6 +66,7 @@ public class AppOpsControllerImpl implements AppOpsController,
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
+ private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
@GuardedBy("mActiveItems")
@@ -82,8 +87,11 @@ public class AppOpsControllerImpl implements AppOpsController,
public AppOpsControllerImpl(
Context context,
@Background Looper bgLooper,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ PermissionFlagsCache cache
+ ) {
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mFlagsCache = cache;
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
@@ -230,10 +238,66 @@ public class AppOpsControllerImpl implements AppOpsController,
}
/**
+ * Does the app-op code refer to a user sensitive permission for the specified user id
+ * and package. Only user sensitive permission should be shown to the user by default.
+ *
+ * @param appOpCode The code of the app-op.
+ * @param uid The uid of the user.
+ * @param packageName The name of the package.
+ *
+ * @return {@code true} iff the app-op item is user sensitive
+ */
+ private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
+ String permission = AppOpsManager.opToPermission(appOpCode);
+ if (permission == null) {
+ return false;
+ }
+ int permFlags = mFlagsCache.getPermissionFlags(permission,
+ packageName, uid);
+ return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+ }
+
+ /**
+ * Does the app-op item refer to an operation that should be shown to the user.
+ * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
+ * permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op item should be shown to the user
+ */
+ private boolean isUserVisible(AppOpItem item) {
+ return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
+ }
+
+
+ /**
+ * Does the app-op, uid and package name, refer to an operation that should be shown to the
+ * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
+ * ops that refer to user sensitive permission should be shown to the user by default.
+ *
+ * @param item The item
+ *
+ * @return {@code true} iff the app-op for should be shown to the user
+ */
+ private boolean isUserVisible(int appOpCode, int uid, String packageName) {
+ // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
+ // which may be user senstive, so for now always show it to the user.
+ if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+ return true;
+ }
+
+ return isUserSensitive(appOpCode, uid, packageName);
+ }
+
+ /**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @return List of active AppOps information
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOps() {
return getActiveAppOpsForUser(UserHandle.USER_ALL);
}
@@ -242,18 +306,23 @@ public class AppOpsControllerImpl implements AppOpsController,
* Returns a copy of the list containing all the active AppOps that the controller tracks, for
* a given user id.
*
+ * Call from a worker thread as it may perform long operations.
+ *
* @param userId User id to track, can be {@link UserHandle#USER_ALL}
*
* @return List of active AppOps information for that user id
*/
+ @WorkerThread
public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+ Assert.isNotMainThread();
List<AppOpItem> list = new ArrayList<>();
synchronized (mActiveItems) {
final int numActiveItems = mActiveItems.size();
for (int i = 0; i < numActiveItems; i++) {
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -263,7 +332,8 @@ public class AppOpsControllerImpl implements AppOpsController,
for (int i = 0; i < numNotedItems; i++) {
AppOpItem item = mNotedItems.get(i);
if ((userId == UserHandle.USER_ALL
- || UserHandle.getUserId(item.getUid()) == userId)) {
+ || UserHandle.getUserId(item.getUid()) == userId)
+ && isUserVisible(item)) {
list.add(item);
}
}
@@ -311,7 +381,7 @@ public class AppOpsControllerImpl implements AppOpsController,
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.contains(code)) {
+ if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
new file mode 100644
index 000000000000..45ed78f750be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private data class PermissionFlagKey(
+ val permission: String,
+ val packageName: String,
+ val uid: Int
+)
+
+/**
+ * Cache for PackageManager's PermissionFlags.
+ *
+ * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
+ * and changes to the uid will trigger new requests (in the background).
+ */
+@Singleton
+class PermissionFlagsCache @Inject constructor(
+ private val packageManager: PackageManager,
+ @Background private val executor: Executor
+) : PackageManager.OnPermissionsChangedListener {
+
+ private val permissionFlagsCache =
+ mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
+ private var listening = false
+
+ override fun onPermissionsChanged(uid: Int) {
+ executor.execute {
+ // Only track those that we've seen before
+ val keys = permissionFlagsCache.get(uid)
+ if (keys != null) {
+ keys.mapValuesTo(keys) {
+ getFlags(it.key)
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve permission flags from cache or PackageManager. There parameters will be passed
+ * directly to [PackageManager].
+ *
+ * Calls to this method should be done from a background thread (though it will only be
+ * enforced if the cache is not hit).
+ */
+ @WorkerThread
+ fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
+ if (!listening) {
+ listening = true
+ packageManager.addOnPermissionsChangeListener(this)
+ }
+ val key = PermissionFlagKey(permission, packageName, uid)
+ return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
+ getFlags(key).also {
+ Assert.isNotMainThread()
+ permissionFlagsCache.get(uid)?.put(key, it)
+ }
+ }
+ }
+
+ private fun getFlags(key: PermissionFlagKey): Int {
+ return packageManager.getPermissionFlags(key.permission, key.packageName,
+ UserHandle.getUserHandleForUid(key.uid))
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 980e4c0fd333..361ea674cead 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,7 +29,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -39,12 +38,10 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.IFingerprintService;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import android.view.WindowManager;
@@ -247,6 +244,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
IActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getService();
}
+
+ FingerprintManager getFingerprintManager(Context context) {
+ return context.getSystemService(FingerprintManager.class);
+ }
}
@Inject
@@ -273,7 +274,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mActivityTaskManager = mInjector.getActivityTaskManager();
- final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+ final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
if (fpm != null && fpm.isHardwareDetected()) {
final List<FingerprintSensorProperties> fingerprintSensorProperties =
fpm.getSensorProperties();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
deleted file mode 100644
index 9db371e487c7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
+++ /dev/null
@@ -1,148 +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 com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.systemui.R;
-
-/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
-public class BubbleDismissView extends FrameLayout {
- /** Duration for animations involving the dismiss target text/icon. */
- private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
- private static final float SCALE_FOR_POP = 1.2f;
- private static final float SCALE_FOR_DISMISS = 0.9f;
-
- private LinearLayout mDismissTarget;
- private ImageView mDismissIcon;
- private View mDismissCircle;
-
- private SpringAnimation mDismissTargetAlphaSpring;
- private SpringAnimation mDismissTargetVerticalSpring;
-
- public BubbleDismissView(Context context) {
- super(context);
- setVisibility(GONE);
-
- LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
- mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
- mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
- mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
-
- // Set up the basic target area animations. These are very simple animations that don't need
- // fancy interpolators.
- final AccelerateDecelerateInterpolator interpolator =
- new AccelerateDecelerateInterpolator();
- mDismissIcon.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
- .setInterpolator(interpolator);
- mDismissCircle.animate()
- .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
- .setInterpolator(interpolator);
-
- mDismissTargetAlphaSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
- mDismissTargetVerticalSpring =
- new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
- .setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
- mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
- // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
- // exactly zero when this listener is triggered. However, if it's less than 50% we can
- // safely assume it was animating out rather than in.
- if (alpha < 0.5f) {
- // If the alpha spring was animating the view out, set it to GONE when it's done.
- setVisibility(INVISIBLE);
- }
- });
- }
-
- /** Springs in the dismiss target. */
- void springIn() {
- setVisibility(View.VISIBLE);
-
- // Fade in the dismiss target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f);
- mDismissTarget.setAlpha(0f);
- mDismissTargetAlphaSpring.animateToFinalPosition(1f);
-
- // Spring up the dismiss target.
- mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
- mDismissTargetVerticalSpring.animateToFinalPosition(0);
-
- mDismissCircle.setAlpha(0f);
- mDismissCircle.setScaleX(SCALE_FOR_POP);
- mDismissCircle.setScaleY(SCALE_FOR_POP);
-
- // Fade in circle and reduce size.
- mDismissCircle.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f);
- }
-
- /** Springs out the dismiss target. */
- void springOut() {
- // Fade out the target icon.
- mDismissIcon.animate()
- .setDuration(50)
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
-
- // Fade out the target.
- mDismissTargetAlphaSpring.animateToFinalPosition(0f);
-
- // Spring the target down a bit.
- mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
-
- // Pop out the circle.
- mDismissCircle.animate()
- .scaleX(SCALE_FOR_DISMISS)
- .scaleY(SCALE_FOR_DISMISS)
- .alpha(0f);
- }
-
- /** Returns the Y value of the center of the dismiss target. */
- float getDismissTargetCenterY() {
- return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
- }
-
- /** Returns the dismiss target, which contains the text/icon and any added padding. */
- View getDismissTarget() {
- return mDismissTarget;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba5248a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
- private View mManageView;
- private TextView mTitleTextView;
- private TextView mDescTextView;
-
- public BubbleManageEducationView(Context context) {
- this(context, null);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mManageView = findViewById(R.id.manage_education_view);
- mTitleTextView = findViewById(R.id.user_education_title);
- mDescTextView = findViewById(R.id.user_education_description);
-
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
-
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
- mTitleTextView.setTextColor(textColor);
- mDescTextView.setTextColor(textColor);
- }
-
- /**
- * Specifies the position for the manage view.
- */
- public void setManageViewPosition(int x, int y) {
- mManageView.setTranslationX(x);
- mManageView.setTranslationY(y);
- }
-
- /**
- * @return the height of the view that shows the educational text and pointer.
- */
- public int getManageViewHeight() {
- return mManageView.getHeight();
- }
-
- @Override
- public void setLayoutDirection(int direction) {
- super.setLayoutDirection(direction);
- if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- mTitleTextView.setGravity(Gravity.RIGHT);
- mDescTextView.setGravity(Gravity.RIGHT);
- } else {
- mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- mTitleTextView.setGravity(Gravity.LEFT);
- mDescTextView.setGravity(Gravity.LEFT);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index bb9d1095a37a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
- public static final String KEY = "Overflow";
-
- private BadgedImageView mOverflowBtn;
- private BubbleExpandedView mExpandedView;
- private LayoutInflater mInflater;
- private Context mContext;
- private Bitmap mIcon;
- private Path mPath;
- private int mBitmapSize;
- private int mIconBitmapSize;
- private int mDotColor;
-
- public BubbleOverflow(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- }
-
- void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
- updateDimensions();
- mExpandedView = (BubbleExpandedView) mInflater.inflate(
- R.layout.bubble_expanded_view, parentViewGroup /* root */,
- false /* attachToRoot */);
- mExpandedView.setOverflow(true);
- mExpandedView.setStackView(stackView);
- mExpandedView.applyThemeAttrs();
- updateIcon(mContext, parentViewGroup);
- }
-
- void updateDimensions() {
- mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_bitmap_size);
- if (mExpandedView != null) {
- mExpandedView.updateDimensions();
- }
- }
-
- void updateIcon(Context context, ViewGroup parentViewGroup) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
- parentViewGroup /* root */,
- false /* attachToRoot */);
- mOverflowBtn.setContentDescription(mContext.getResources().getString(
- R.string.bubble_overflow_button_content_description));
- Resources res = mContext.getResources();
-
- // Set color for button icon and dot
- TypedValue typedValue = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
- int colorAccent = mContext.getColor(typedValue.resourceId);
- mOverflowBtn.getDrawable().setTint(colorAccent);
- mDotColor = colorAccent;
-
- // Set color for button and activity background
- ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
- final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- if (mode == Configuration.UI_MODE_NIGHT_YES) {
- bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
- }
-
- // Apply icon inset
- InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
- mBitmapSize - mIconBitmapSize /* inset */);
- AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
- BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
- mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
-
- // Get path with dot location
- float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = DEFAULT_PATH_SIZE / 2f;
- mPath = PathParser.createPathFromPathData(
- mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- mPath.transform(matrix);
-
- mOverflowBtn.setRenderedBubble(this);
- }
-
- void setVisible(int visible) {
- mOverflowBtn.setVisibility(visible);
- }
-
- @Override
- public BubbleExpandedView getExpandedView() {
- return mExpandedView;
- }
-
- @Override
- public int getDotColor() {
- return mDotColor;
- }
-
- @Override
- public Bitmap getBadgedImage() {
- return mIcon;
- }
-
- @Override
- public boolean showDot() {
- return false;
- }
-
- @Override
- public Path getDotPath() {
- return mPath;
- }
-
- @Override
- public void setContentVisibility(boolean visible) {
- mExpandedView.setContentVisibility(visible);
- }
-
- @Override
- public View getIconView() {
- return mOverflowBtn;
- }
-
- @Override
- public String getKey() {
- return BubbleOverflow.KEY;
- }
-
- @Override
- public int getDisplayId() {
- return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 000000000000..155b71b99ff9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+ private val context: Context,
+ private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+ private var bitmap: Bitmap? = null
+ private var dotPath: Path? = null
+ private var bitmapSize = 0
+ private var iconBitmapSize = 0
+ private var dotColor = 0
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ private val expandedView: BubbleExpandedView = inflater
+ .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+ as BubbleExpandedView
+ private val overflowBtn: BadgedImageView = inflater
+ .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+ as BadgedImageView
+ init {
+ updateResources()
+ with(expandedView) {
+ setOverflow(true)
+ setStackView(stack)
+ applyThemeAttrs()
+ }
+ with(overflowBtn) {
+ setContentDescription(context.resources.getString(
+ R.string.bubble_overflow_button_content_description))
+ updateBtnTheme()
+ }
+ }
+
+ fun update() {
+ updateResources()
+ expandedView.applyThemeAttrs()
+ // Apply inset and new style to fresh icon drawable.
+ overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+ updateBtnTheme()
+ }
+
+ fun updateResources() {
+ bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+ iconBitmapSize = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_bitmap_size)
+ val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+ overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+ expandedView.updateDimensions()
+ }
+
+ fun updateBtnTheme() {
+ val res = context.resources
+
+ // Set overflow button accent color, dot color
+ val typedValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+ val colorAccent = res.getColor(typedValue.resourceId)
+ overflowBtn.getDrawable()?.setTint(colorAccent)
+ dotColor = colorAccent
+
+ // Set button and activity background color
+ val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ == Configuration.UI_MODE_NIGHT_YES)
+ val bg = ColorDrawable(res.getColor(
+ if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+ // Set button icon
+ val iconFactory = BubbleIconFactory(context)
+ val fg = InsetDrawable(overflowBtn.getDrawable(),
+ bitmapSize - iconBitmapSize /* inset */)
+ bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+ null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+ // Set dot path
+ dotPath = PathParser.createPathFromPathData(
+ res.getString(com.android.internal.R.string.config_icon_mask))
+ val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+ null /* outBounds */, null /* path */, null /* outMaskShape */)
+ val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+ val matrix = Matrix()
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */)
+ dotPath?.transform(matrix)
+ overflowBtn.setRenderedBubble(this)
+ }
+
+ fun setVisible(visible: Int) {
+ overflowBtn.visibility = visible
+ }
+
+ override fun getExpandedView(): BubbleExpandedView? {
+ return expandedView
+ }
+
+ override fun getDotColor(): Int {
+ return dotColor
+ }
+
+ override fun getBadgedImage(): Bitmap? {
+ return bitmap
+ }
+
+ override fun showDot(): Boolean {
+ return false
+ }
+
+ override fun getDotPath(): Path? {
+ return dotPath
+ }
+
+ override fun setContentVisibility(visible: Boolean) {
+ expandedView.setContentVisibility(visible)
+ }
+
+ override fun getIconView(): View? {
+ return overflowBtn
+ }
+
+ override fun getKey(): String {
+ return KEY
+ }
+
+ override fun getDisplayId(): Int {
+ return expandedView.virtualDisplayId
+ }
+
+ companion object {
+ @JvmField val KEY = "Overflow"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f02945ef843a..ea12c9598b91 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -48,7 +48,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -95,7 +94,6 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -118,7 +116,7 @@ public class BubbleStackView extends FrameLayout
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** Animation durations for bubble stack user education views. **/
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+ static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -139,9 +137,6 @@ public class BubbleStackView extends FrameLayout
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
- /** Duration of the dismiss scrim fading in/out. */
- private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -300,7 +295,7 @@ public class BubbleStackView extends FrameLayout
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
- pw.print(" showingDismiss: "); pw.println(mShowingDismiss);
+ pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
pw.print(" isExpansionAnimating: "); pw.println(mIsExpansionAnimating);
pw.print(" expandedContainerVis: "); pw.println(mExpandedViewContainer.getVisibility());
pw.print(" expandedContainerAlpha: "); pw.println(mExpandedViewContainer.getAlpha());
@@ -347,7 +342,6 @@ public class BubbleStackView extends FrameLayout
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- private boolean mShowingDismiss = false;
/** The view to desaturate/darken when magneted to the dismiss target. */
@Nullable private View mDesaturateAndDarkenTargetView;
@@ -465,7 +459,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mExpandedAnimationController.snapBubbleBack(
mExpandedAnimationController.getDraggedOutBubble(), velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mExpandedAnimationController.onUnstuckFromTarget();
}
@@ -479,9 +473,9 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
BubbleStackView.this::dismissMagnetizedObject /* after */);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -502,7 +496,7 @@ public class BubbleStackView extends FrameLayout
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
- hideDismissTarget();
+ mDismissView.hide();
} else {
mStackAnimationController.onUnstuckFromTarget();
}
@@ -511,14 +505,14 @@ public class BubbleStackView extends FrameLayout
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mStackAnimationController.animateStackDismissal(
- mDismissTargetContainer.getHeight() /* translationYBy */,
+ mDismissView.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
}
);
- hideDismissTarget();
+ mDismissView.hide();
}
};
@@ -639,7 +633,7 @@ public class BubbleStackView extends FrameLayout
}
// Show the dismiss target, if we haven't already.
- springInDismissTargetMaybe();
+ mDismissView.show();
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
@@ -681,7 +675,7 @@ public class BubbleStackView extends FrameLayout
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
- hideDismissTarget();
+ mDismissView.hide();
}
mIsDraggingStack = false;
@@ -743,12 +737,7 @@ public class BubbleStackView extends FrameLayout
}
};
- private View mDismissTargetCircle;
- private ViewGroup mDismissTargetContainer;
- private PhysicsAnimator<View> mDismissTargetAnimator;
- private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
+ private DismissView mDismissView;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@Nullable
@@ -759,7 +748,7 @@ public class BubbleStackView extends FrameLayout
private View mUserEducationView;
private boolean mShouldShowManageEducation;
- private BubbleManageEducationView mManageEducationView;
+ private ManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
private ViewGroup mManageMenu;
@@ -866,34 +855,8 @@ public class BubbleStackView extends FrameLayout
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle = new DismissCircleView(context);
- final FrameLayout.LayoutParams newParams =
- new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- mDismissTargetCircle.setLayoutParams(newParams);
- mDismissTargetAnimator = PhysicsAnimator.getInstance(mDismissTargetCircle);
-
- mDismissTargetContainer = new FrameLayout(context);
- mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
- MATCH_PARENT,
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
- Gravity.BOTTOM));
-
- final int bottomMargin =
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
- mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
- mDismissTargetContainer.setClipToPadding(false);
- mDismissTargetContainer.setClipChildren(false);
- mDismissTargetContainer.addView(mDismissTargetCircle);
- mDismissTargetContainer.setVisibility(View.INVISIBLE);
- mDismissTargetContainer.setBackgroundResource(
- R.drawable.floating_dismiss_gradient_transition);
- addView(mDismissTargetContainer);
-
- // Start translated down so the target springs up.
- mDismissTargetCircle.setTranslationY(
- getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
+ mDismissView = new DismissView(context);
+ addView(mDismissView);
final ContentResolver contentResolver = getContext().getContentResolver();
final int dismissRadius = Settings.Secure.getInt(
@@ -901,13 +864,23 @@ public class BubbleStackView extends FrameLayout
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
// MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
setClipChildren(false);
setFocusable(true);
mBubbleContainer.bringToFront();
- setUpOverflow();
+ mBubbleOverflow = new BubbleOverflow(getContext(), this);
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() /* index */,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ updateOverflow();
+ mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+ setSelectedBubble(mBubbleOverflow);
+ showManageMenu(false);
+ });
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -933,7 +906,7 @@ public class BubbleStackView extends FrameLayout
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
- mBubbleOverflow.updateDimensions();
+ mBubbleOverflow.updateResources();
// Need to update the padding around the view
WindowInsets insets = getRootWindowInsets();
@@ -1162,12 +1135,9 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
}
if (mShouldShowManageEducation) {
- mManageEducationView = (BubbleManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+ mManageEducationView = (ManageEducationView)
+ mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
false /* attachToRoot */);
- mManageEducationView.setVisibility(GONE);
- mManageEducationView.setElevation(mBubbleElevation);
- mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
addView(mManageEducationView);
}
}
@@ -1187,32 +1157,21 @@ public class BubbleStackView extends FrameLayout
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
- private void setUpOverflow() {
- int overflowBtnIndex = 0;
- if (mBubbleOverflow == null) {
- mBubbleOverflow = new BubbleOverflow(getContext());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- } else {
- mBubbleContainer.removeView(mBubbleOverflow.getIconView());
- mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
- overflowBtnIndex = mBubbleContainer.getChildCount();
- }
- mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mBubbleOverflow.getIconView().setOnClickListener(v -> {
- setSelectedBubble(mBubbleOverflow);
- showManageMenu(false);
- });
+ private void updateOverflow() {
+ mBubbleOverflow.update();
+ mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() - 1 /* index */);
updateOverflowVisibility();
}
+
/**
* Handle theme changes.
*/
public void onThemeChanged() {
setUpFlyout();
- setUpOverflow();
setUpUserEducation();
setUpManageMenu();
+ updateOverflow();
updateExpandedViewTheme();
}
@@ -1261,7 +1220,7 @@ public class BubbleStackView extends FrameLayout
/** Respond to the display size change by recalculating view size and location. */
public void onDisplaySizeChanged() {
- setUpOverflow();
+ updateOverflow();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealSize(mDisplaySize);
@@ -1279,12 +1238,7 @@ public class BubbleStackView extends FrameLayout
}
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
-
- final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
- mDismissTargetCircle.getLayoutParams().width = targetSize;
- mDismissTargetCircle.getLayoutParams().height = targetSize;
- mDismissTargetCircle.requestLayout();
-
+ mDismissView.updateResources();
mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
}
@@ -1796,28 +1750,8 @@ public class BubbleStackView extends FrameLayout
&& mManageEducationView.getVisibility() != VISIBLE
&& mIsExpanded
&& mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.setAlpha(0);
- mManageEducationView.setVisibility(VISIBLE);
- mManageEducationView.post(() -> {
- mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
- final int viewHeight = mManageEducationView.getManageViewHeight();
- final int inset = getResources().getDimensionPixelSize(
- R.dimen.bubbles_manage_education_top_inset);
- mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
- mManageEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
- mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
- mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
- .performClick();
- maybeShowManageEducation(false);
- });
- mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
- maybeShowManageEducation(false));
- mManageEducationView.setOnClickListener(view ->
- maybeShowManageEducation(false));
- });
+ mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+ () -> maybeShowManageEducation(false) /* run on click */);
Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
} else if (!show
&& mManageEducationView.getVisibility() == VISIBLE
@@ -2362,48 +2296,6 @@ public class BubbleStackView extends FrameLayout
}
}
- /** Animates in the dismiss target. */
- private void springInDismissTargetMaybe() {
- if (mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = true;
-
- mDismissTargetContainer.bringToFront();
- mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
- mDismissTargetContainer.setVisibility(VISIBLE);
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator.cancel();
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
- .start();
- }
-
- /**
- * Animates the dismiss target out, as well as the circle that encircles the bubbles, if they
- * were dragged into the target and encircled.
- */
- private void hideDismissTarget() {
- if (!mShowingDismiss) {
- return;
- }
-
- mShowingDismiss = false;
-
- ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
- DISMISS_TRANSITION_DURATION_MS);
-
- mDismissTargetAnimator
- .spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
- mDismissTargetSpring)
- .withEndActions(() -> mDismissTargetContainer.setVisibility(View.INVISIBLE))
- .start();
- }
-
/** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
private void animateFlyoutCollapsed(boolean collapsed, float velX) {
final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
new file mode 100644
index 000000000000..71faf4a2eeb7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.drawable.TransitionDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.systemui.R
+import com.android.systemui.util.DismissCircleView
+import com.android.systemui.util.animation.PhysicsAnimator
+
+/*
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+
+ var circle = DismissCircleView(context).apply {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ val newParams = LayoutParams(targetSize, targetSize)
+ newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ setLayoutParams(newParams)
+ setTranslationY(
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
+ }
+
+ var isShowing = false
+ private val animator = PhysicsAnimator.getInstance(circle)
+ private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
+ private val DISMISS_SCRIM_FADE_MS = 200
+ init {
+ setLayoutParams(LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
+ Gravity.BOTTOM))
+ setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ setClipToPadding(false)
+ setClipChildren(false)
+ setVisibility(View.INVISIBLE)
+ setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition)
+ addView(circle)
+ }
+
+ /**
+ * Animates this view in.
+ */
+ fun show() {
+ if (isShowing) return
+ isShowing = true
+ bringToFront()
+ setZ(Short.MAX_VALUE - 1f)
+ setVisibility(View.VISIBLE)
+ (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+ animator.cancel()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+ .start()
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they
+ * were dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ isShowing = false
+ (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+ spring)
+ .withEndActions({ setVisibility(View.INVISIBLE) })
+ .start()
+ }
+
+ fun updateResources() {
+ val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ circle.layoutParams.width = targetSize
+ circle.layoutParams.height = targetSize
+ circle.requestLayout()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 000000000000..c58ab31c4561
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+ context: Context?,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+ private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+ private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+ private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+ private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+ private var isInflated = false
+
+ init {
+ this.visibility = View.GONE
+ this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+ this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ override fun setLayoutDirection(direction: Int) {
+ super.setLayoutDirection(direction)
+ // setLayoutDirection runs before onFinishInflate
+ // so skip if views haven't inflated; otherwise we'll get NPEs
+ if (!isInflated) return
+ setDirection()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ isInflated = true
+ setDirection()
+ setTextColor()
+ }
+
+ private fun setTextColor() {
+ val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse))
+ val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+ var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+ typedArray.recycle()
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+ titleTextView.setTextColor(textColor)
+ descTextView.setTextColor(textColor)
+ }
+
+ fun setDirection() {
+ manageView.setBackgroundResource(
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+ R.drawable.bubble_stack_user_education_bg_rtl
+ else R.drawable.bubble_stack_user_education_bg)
+ titleTextView.gravity = Gravity.START
+ descTextView.gravity = Gravity.START
+ }
+
+ fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+ alpha = 0f
+ visibility = View.VISIBLE
+ post {
+ expandedView.getManageButtonBoundsOnScreen(rect)
+ with(hideMenu) {
+ manageButton
+ .setOnClickListener {
+ expandedView.findViewById<View>(R.id.settings_button).performClick()
+ this.run()
+ }
+ gotItButton.setOnClickListener { this.run() }
+ setOnClickListener { this.run() }
+ }
+ with(manageView) {
+ translationX = 0f
+ val inset = resources.getDimensionPixelSize(
+ R.dimen.bubbles_manage_education_top_inset)
+ translationY = (rect.top - manageView.height + inset).toFloat()
+ }
+ bringToFront()
+ animate()
+ .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 58807f0f7025..aa3e193ddba2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -49,6 +49,11 @@ open class ControlsBindingControllerImpl @Inject constructor(
private const val SUGGESTED_STRUCTURES = 6L
private const val SUGGESTED_CONTROLS_REQUEST =
ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE * SUGGESTED_STRUCTURES
+
+ private val emptyCallback = object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {}
+ override fun error(message: String) {}
+ }
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -283,7 +288,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback,
+ var callback: ControlsBindingController.LoadCallback,
val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
@@ -337,6 +342,10 @@ open class ControlsBindingControllerImpl @Inject constructor(
if (isTerminated.get()) return
_loadCancelInternal = {}
+
+ // Reassign the callback to clear references to other areas of code. Binders such as
+ // this may not be GC'd right away, so do not hold onto these references.
+ callback = emptyCallback
currentProvider?.cancelLoadTimeout()
backgroundExecutor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 56d0fa237b82..6e8d63b2c516 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,7 +18,9 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
-import com.android.systemui.screenshot.GlobalScreenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.DeleteScreenshotReceiver;
+import com.android.systemui.screenshot.SmartActionsReceiver;
import dagger.Binds;
import dagger.Module;
@@ -30,10 +32,31 @@ import dagger.multibindings.IntoMap;
*/
@Module
public abstract class DefaultBroadcastReceiverBinder {
- /** */
+ /**
+ *
+ */
@Binds
@IntoMap
@ClassKey(ActionProxyReceiver.class)
public abstract BroadcastReceiver bindActionProxyReceiver(
ActionProxyReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(DeleteScreenshotReceiver.class)
+ public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
+ DeleteScreenshotReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(SmartActionsReceiver.class)
+ public abstract BroadcastReceiver bindSmartActionsReceiver(
+ SmartActionsReceiver broadcastReceiver);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index cd0ba290db46..803e56db8ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.wmshell.WindowManagerShellModule;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -71,7 +72,7 @@ import dagger.Provides;
* A dagger module for injecting default implementations of components of System UI that may be
* overridden by the System UI implementation.
*/
-@Module(includes = {DividerModule.class, QSModule.class})
+@Module(includes = {DividerModule.class, QSModule.class, WindowManagerShellModule.class})
public abstract class SystemUIDefaultModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4bd046e23dab..fce545b421d5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.SensorModule;
+import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -65,7 +66,8 @@ import dagger.Provides;
LogModule.class,
PeopleHubModule.class,
SensorModule.class,
- SettingsModule.class
+ SettingsModule.class,
+ SettingsUtilModule.class
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index b9d23ade2ee1..1ef806c8bd68 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -364,9 +364,6 @@ public class DozeMachine {
Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
return mState;
}
- if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
- return State.DOZE;
- }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 37bdda8a06a1..524d9c8536b8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -21,7 +21,6 @@ import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_
import android.annotation.AnyThread;
import android.app.ActivityManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -49,6 +48,7 @@ import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -64,10 +64,10 @@ public class DozeSensors {
private final Context mContext;
private final AsyncSensorManager mSensorManager;
- private final ContentResolver mResolver;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
+ private final SecureSettings mSecureSettings;
private final Callback mCallback;
@VisibleForTesting
protected TriggerSensor[] mSensors;
@@ -98,13 +98,13 @@ public class DozeSensors {
DozeSensors(Context context, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
- ProximitySensor proximitySensor) {
+ ProximitySensor proximitySensor, SecureSettings secureSettings) {
mContext = context;
mSensorManager = sensorManager;
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
- mResolver = mContext.getContentResolver();
+ mSecureSettings = secureSettings;
mCallback = callback;
mProximitySensor = proximitySensor;
@@ -241,7 +241,7 @@ public class DozeSensors {
}
if (!anyListening) {
- mResolver.unregisterContentObserver(mSettingsObserver);
+ mSecureSettings.unregisterContentObserver(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mSensors) {
s.registerSettingsObserver(mSettingsObserver);
@@ -400,7 +400,7 @@ public class DozeSensors {
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
- return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
+ return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
UserHandle.USER_CURRENT) != 0;
}
@@ -444,9 +444,8 @@ public class DozeSensors {
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
- mResolver.registerContentObserver(
- Settings.Secure.getUriFor(mSetting), false /* descendants */,
- mSettingsObserver, UserHandle.USER_ALL);
+ mSecureSettings.registerContentObserverForUser(
+ mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index abbbd1713250..e38dce05a32e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -163,7 +164,8 @@ public class DozeTriggers implements DozeMachine.Part {
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
- DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
+ DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
+ SecureSettings secureSettings) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -172,7 +174,8 @@ public class DozeTriggers implements DozeMachine.Part {
mWakeLock = wakeLock;
mAllowPulseTriggers = true;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
+ config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
+ secureSettings);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 662831e4a445..24ca9708a4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -27,6 +27,7 @@ import javax.inject.Inject
import javax.inject.Singleton
private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
/**
* Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -98,7 +99,7 @@ class MediaDataFilter @Inject constructor(
// are up to date
mediaEntries.clear()
keyCopy.forEach {
- Log.d(TAG, "Removing $it after user change")
+ if (DEBUG) Log.d(TAG, "Removing $it after user change")
listenersCopy.forEach { listener ->
listener.onMediaDataRemoved(it)
}
@@ -106,7 +107,7 @@ class MediaDataFilter @Inject constructor(
dataSource.getData().forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- Log.d(TAG, "Re-adding $key after user change")
+ if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
mediaEntries.put(key, data)
listenersCopy.forEach { listener ->
listener.onMediaDataLoaded(key, null, data)
@@ -119,6 +120,7 @@ class MediaDataFilter @Inject constructor(
* Invoked when the user has dismissed the media carousel
*/
fun onSwipeToDismiss() {
+ if (DEBUG) Log.d(TAG, "Media carousel swiped away")
val mediaKeys = mediaEntries.keys.toSet()
mediaKeys.forEach {
mediaDataManager.setTimedOut(it, timedOut = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b3277737f397..d82150f2346b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,6 +63,7 @@ private val ART_URIS = arrayOf(
)
private const val TAG = "MediaDataManager"
+private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
@@ -253,7 +254,7 @@ class MediaDataManager(
fun removeListener(listener: Listener) = listeners.remove(listener)
/**
- * Called whenever the player has been paused or stopped for a while.
+ * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
* This will make the player not active anymore, hiding it from QQS and Keyguard.
* @see MediaData.active
*/
@@ -263,6 +264,7 @@ class MediaDataManager(
return
}
it.active = !timedOut
+ if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
onMediaDataLoaded(token, token, it)
}
}
@@ -283,7 +285,9 @@ class MediaDataManager(
return
}
- Log.d(TAG, "adding track for $userId from browser: $desc")
+ if (DEBUG) {
+ Log.d(TAG, "adding track for $userId from browser: $desc")
+ }
// Album art
var artworkBitmap = desc.iconBitmap
@@ -383,7 +387,7 @@ class MediaDataManager(
if (actions != null) {
for ((index, action) in actions.withIndex()) {
if (action.getIcon() == null) {
- Log.i(TAG, "No icon for action $index ${action.title}")
+ if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
actionsToShowCollapsed.remove(index)
continue
}
@@ -427,7 +431,7 @@ class MediaDataManager(
if (!TextUtils.isEmpty(uriString)) {
val albumArt = loadBitmapFromUri(Uri.parse(uriString))
if (albumArt != null) {
- Log.d(TAG, "loaded art from $uri")
+ if (DEBUG) Log.d(TAG, "loaded art from $uri")
return albumArt
}
}
@@ -514,7 +518,7 @@ class MediaDataManager(
Assert.isMainThread()
val removed = mediaEntries.remove(key)
if (useMediaResumption && removed?.resumeAction != null) {
- Log.d(TAG, "Not removing $key because resumable")
+ if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
val updated = removed.copy(token = null, actions = listOf(resumeAction),
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 16e05f1e3f26..c0b9258f39fd 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -27,7 +27,6 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -39,7 +38,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
index f995bf9eb2a1..71c5f8020330 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -41,8 +41,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import javax.inject.Inject;
import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 586761b0fc3d..70a81aaed249 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -33,8 +33,8 @@ import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 665b90e29976..df3aeadaacd6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -39,8 +39,8 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.window.WindowContainerTransaction;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 35e56ee87967..312d6d62128f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -58,8 +58,8 @@ import android.window.WindowOrganizer;
import com.android.internal.os.SomeArgs;
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.wm.DisplayController;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 02bf475d5744..582cd046f9e0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -56,9 +56,8 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
@@ -79,7 +78,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
private final Rect mTmpNormalBounds = new Rect();
- private final Rect mReentryBounds = new Rect();
+ protected final Rect mReentryBounds = new Rect();
private PipBoundsHandler mPipBoundsHandler;
private InputConsumerController mInputConsumerController;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
new file mode 100644
index 000000000000..870e714ee24c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+class OngoingPrivacyChip @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttrs: Int = 0,
+ defStyleRes: Int = 0
+) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+ private val iconMarginExpanded = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_expanded)
+ private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_icon_margin_collapsed)
+ private val iconSize =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ private val iconColor = context.resources.getColor(
+ R.color.status_bar_clock_color, context.theme)
+ private val sidePadding =
+ context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
+ private lateinit var iconsContainer: LinearLayout
+ private lateinit var back: FrameLayout
+ var expanded = false
+ set(value) {
+ if (value != field) {
+ field = value
+ updateView(PrivacyChipBuilder(context, privacyList))
+ }
+ }
+
+ var privacyList = emptyList<PrivacyItem>()
+ set(value) {
+ field = value
+ updateView(PrivacyChipBuilder(context, field))
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ back = requireViewById(R.id.background)
+ iconsContainer = requireViewById(R.id.icons_container)
+ }
+
+ // Should only be called if the builder icons or app changed
+ private fun updateView(builder: PrivacyChipBuilder) {
+ back.background = if (expanded) backgroundDrawable else null
+ val padding = if (expanded) sidePadding else 0
+ back.setPaddingRelative(padding, 0, padding, 0)
+ fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
+ iconsContainer.removeAllViews()
+ chipBuilder.generateIcons().forEachIndexed { i, it ->
+ it.mutate()
+ it.setTint(iconColor)
+ val image = ImageView(context).apply {
+ setImageDrawable(it)
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ iconsContainer.addView(image, iconSize, iconSize)
+ if (i != 0) {
+ val lp = image.layoutParams as MarginLayoutParams
+ lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+ image.layoutParams = lp
+ }
+ }
+ }
+
+ if (!privacyList.isEmpty()) {
+ generateContentDescription(builder)
+ setIcons(builder, iconsContainer)
+ val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.CENTER_VERTICAL or
+ (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
+ iconsContainer.layoutParams = lp
+ } else {
+ iconsContainer.removeAllViews()
+ }
+ requestLayout()
+ }
+
+ private fun generateContentDescription(builder: PrivacyChipBuilder) {
+ val typesText = builder.joinTypes()
+ contentDescription = context.getString(
+ R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
new file mode 100644
index 000000000000..1d2e74703b42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
+
+ val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+ val types: List<PrivacyType>
+ private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+ private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
+
+ init {
+ appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+ .toList()
+ .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+ { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ types = itemsList.map { it.privacyType }.distinct().sorted()
+ }
+
+ fun generateIcons() = types.map { it.getIcon(context) }
+
+ private fun <T> List<T>.joinWithAnd(): StringBuilder {
+ return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+ append(lastSeparator)
+ append(this@joinWithAnd.last())
+ }
+ }
+
+ fun joinTypes(): String {
+ return when (types.size) {
+ 0 -> ""
+ 1 -> types[0].getName(context)
+ else -> types.map { it.getName(context) }.joinWithAnd().toString()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
new file mode 100644
index 000000000000..1f24fde1377e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyChipEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Privacy chip is viewed by the user. Logged at most once per time QS is visible")
+ ONGOING_INDICATORS_CHIP_VIEW(601),
+
+ @UiEvent(doc = "Privacy chip is clicked")
+ ONGOING_INDICATORS_CHIP_CLICK(602);
+
+ override fun getId() = _id
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
new file mode 100644
index 000000000000..3da1363f2a56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+typealias Privacy = PrivacyType
+
+enum class PrivacyType(val nameId: Int, val iconId: Int) {
+ // This is uses the icons used by the corresponding permission groups in the AndroidManifest
+ TYPE_CAMERA(R.string.privacy_type_camera,
+ com.android.internal.R.drawable.perm_group_camera),
+ TYPE_MICROPHONE(R.string.privacy_type_microphone,
+ com.android.internal.R.drawable.perm_group_microphone),
+ TYPE_LOCATION(R.string.privacy_type_location,
+ com.android.internal.R.drawable.perm_group_location);
+
+ fun getName(context: Context) = context.resources.getString(nameId)
+
+ fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
+}
+
+data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication)
+
+data class PrivacyApplication(val packageName: String, val uid: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
new file mode 100644
index 000000000000..d5a14f7bef2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.Dumpable
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PrivacyItemController @Inject constructor(
+ context: Context,
+ private val appOpsController: AppOpsController,
+ @Main uiExecutor: DelayableExecutor,
+ @Background private val bgExecutor: Executor,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val deviceConfigProxy: DeviceConfigProxy,
+ private val userManager: UserManager,
+ dumpManager: DumpManager
+) : Dumpable {
+
+ @VisibleForTesting
+ internal companion object {
+ val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION)
+ val intentFilter = IntentFilter().apply {
+ addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ }
+ const val TAG = "PrivacyItemController"
+ }
+
+ @VisibleForTesting
+ internal var privacyList = emptyList<PrivacyItem>()
+ @Synchronized get() = field.toList() // Returns a shallow copy of the list
+ @Synchronized set
+
+ private fun isPermissionsHubEnabled(): Boolean {
+ return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ }
+
+ private var currentUserIds = emptyList<Int>()
+ private var listening = false
+ private val callbacks = mutableListOf<WeakReference<Callback>>()
+ private val internalUiExecutor = MyExecutor(uiExecutor)
+
+ private val notifyChanges = Runnable {
+ val list = privacyList
+ callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
+ }
+
+ private val updateListAndNotifyChanges = Runnable {
+ updatePrivacyList()
+ uiExecutor.execute(notifyChanges)
+ }
+
+ var indicatorsAvailable = isPermissionsHubEnabled()
+ private set
+ @VisibleForTesting
+ internal val devicePropertiesChangedListener =
+ object : DeviceConfig.OnPropertiesChangedListener {
+ override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+ if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
+ properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
+ val flag = properties.getBoolean(
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ if (indicatorsAvailable != flag) {
+ // This is happening already in the UI executor, so we can iterate in the
+ indicatorsAvailable = flag
+ callbacks.forEach { it.get()?.onFlagChanged(flag) }
+ }
+
+ internalUiExecutor.updateListeningState()
+ }
+ }
+ }
+
+ private val cb = object : AppOpsController.Callback {
+ override fun onActiveStateChanged(
+ code: Int,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ val userId = UserHandle.getUserId(uid)
+ if (userId in currentUserIds) {
+ update(false)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal var userSwitcherReceiver = Receiver()
+ set(value) {
+ unregisterReceiver()
+ field = value
+ if (listening) registerReceiver()
+ }
+
+ init {
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ uiExecutor,
+ devicePropertiesChangedListener)
+ dumpManager.registerDumpable(TAG, this)
+ }
+
+ private fun unregisterReceiver() {
+ broadcastDispatcher.unregisterReceiver(userSwitcherReceiver)
+ }
+
+ private fun registerReceiver() {
+ broadcastDispatcher.registerReceiver(userSwitcherReceiver, intentFilter,
+ null /* handler */, UserHandle.ALL)
+ }
+
+ private fun update(updateUsers: Boolean) {
+ bgExecutor.execute {
+ if (updateUsers) {
+ val currentUser = ActivityManager.getCurrentUser()
+ currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ }
+ updateListAndNotifyChanges.run()
+ }
+ }
+
+ /**
+ * Updates listening status based on whether there are callbacks and the indicators are enabled
+ *
+ * This is only called from private (add/remove)Callback and from the config listener, all in
+ * main thread.
+ */
+ private fun setListeningState() {
+ val listen = !callbacks.isEmpty() and indicatorsAvailable
+ if (listening == listen) return
+ listening = listen
+ if (listening) {
+ appOpsController.addCallback(OPS, cb)
+ registerReceiver()
+ update(true)
+ } else {
+ appOpsController.removeCallback(OPS, cb)
+ unregisterReceiver()
+ // Make sure that we remove all indicators and notify listeners if we are not
+ // listening anymore due to indicators being disabled
+ update(false)
+ }
+ }
+
+ private fun addCallback(callback: WeakReference<Callback>) {
+ callbacks.add(callback)
+ if (callbacks.isNotEmpty() && !listening) {
+ internalUiExecutor.updateListeningState()
+ }
+ // Notify this callback if we didn't set to listening
+ else if (listening) {
+ internalUiExecutor.execute(NotifyChangesToCallback(callback.get(), privacyList))
+ }
+ }
+
+ private fun removeCallback(callback: WeakReference<Callback>) {
+ // Removes also if the callback is null
+ callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+ if (callbacks.isEmpty()) {
+ internalUiExecutor.updateListeningState()
+ }
+ }
+
+ fun addCallback(callback: Callback) {
+ addCallback(WeakReference(callback))
+ }
+
+ fun removeCallback(callback: Callback) {
+ removeCallback(WeakReference(callback))
+ }
+
+ private fun updatePrivacyList() {
+ if (!listening) {
+ privacyList = emptyList()
+ return
+ }
+ val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+ .mapNotNull { toPrivacyItem(it) }.distinct()
+ privacyList = list
+ }
+
+ private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+ val type: PrivacyType = when (appOpItem.code) {
+ AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+ AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+ else -> return null
+ }
+ val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
+ return PrivacyItem(type, app)
+ }
+
+ interface Callback {
+ fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+ @JvmDefault
+ fun onFlagChanged(flag: Boolean) {}
+ }
+
+ internal inner class Receiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intentFilter.hasAction(intent.action)) {
+ update(true)
+ }
+ }
+ }
+
+ private class NotifyChangesToCallback(
+ private val callback: Callback?,
+ private val list: List<PrivacyItem>
+ ) : Runnable {
+ override fun run() {
+ callback?.onPrivacyItemsChanged(list)
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("PrivacyItemController state:")
+ pw.println(" Listening: $listening")
+ pw.println(" Current user ids: $currentUserIds")
+ pw.println(" Privacy Items:")
+ privacyList.forEach {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ pw.println(" Callbacks:")
+ callbacks.forEach {
+ it.get()?.let {
+ pw.print(" ")
+ pw.println(it.toString())
+ }
+ }
+ }
+
+ private inner class MyExecutor(
+ private val delegate: DelayableExecutor
+ ) : Executor {
+
+ private var listeningCanceller: Runnable? = null
+
+ override fun execute(command: Runnable) {
+ delegate.execute(command)
+ }
+
+ fun updateListeningState() {
+ listeningCanceller?.run()
+ listeningCanceller = delegate.executeDelayed({ setListeningState() }, 0L)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b07b1a9561ff..2dc82dd853d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -31,6 +31,7 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.Looper;
import android.provider.AlarmClock;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
@@ -46,7 +47,9 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -55,6 +58,7 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
@@ -63,6 +67,10 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.PrivacyChipEvent;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.qs.carrier.QSCarrierGroup;
import com.android.systemui.statusbar.CommandQueue;
@@ -101,7 +109,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
- private final Handler mHandler = new Handler();
private final NextAlarmController mAlarmController;
private final ZenModeController mZenController;
private final StatusBarIconController mStatusBarIconController;
@@ -140,9 +147,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private View mRingerContainer;
private Clock mClockView;
private DateView mDateView;
+ private OngoingPrivacyChip mPrivacyChip;
+ private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private boolean mPermissionsHubEnabled;
+ private PrivacyItemController mPrivacyItemController;
+ private final UiEventLogger mUiEventLogger;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -156,22 +168,43 @@ public class QuickStatusBarHeader extends RelativeLayout implements
private int mCutOutPaddingRight;
private float mExpandedHeaderAlpha = 1.0f;
private float mKeyguardExpansionFraction;
+ private boolean mPrivacyChipLogged = false;
+
+ private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+ @Override
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ mPrivacyChip.setPrivacyList(privacyItems);
+ setChipVisibility(!privacyItems.isEmpty());
+ }
+
+ @Override
+ public void onFlagChanged(boolean flag) {
+ if (mPermissionsHubEnabled != flag) {
+ StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+ iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+ }
+ }
+ };
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NextAlarmController nextAlarmController, ZenModeController zenModeController,
StatusBarIconController statusBarIconController,
- ActivityStarter activityStarter,
- CommandQueue commandQueue, RingerModeTracker ringerModeTracker) {
+ ActivityStarter activityStarter, PrivacyItemController privacyItemController,
+ CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
+ UiEventLogger uiEventLogger) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
+ mPrivacyItemController = privacyItemController;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -198,8 +231,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mRingerModeTextView = findViewById(R.id.ringer_mode_text);
mRingerContainer = findViewById(R.id.ringer_container);
mRingerContainer.setOnClickListener(this::onClick);
+ mPrivacyChip = findViewById(R.id.privacy_chip);
+ mPrivacyChip.setOnClickListener(this::onClick);
mCarrierGroup = findViewById(R.id.carrier_group);
+
updateResources();
Rect tintArea = new Rect(0, 0, 0, 0);
@@ -219,6 +255,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
+ mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -229,6 +266,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
+
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
}
public QuickQSPanel getHeaderQsPanel() {
@@ -241,6 +280,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
com.android.internal.R.string.status_bar_camera));
ignored.add(mContext.getResources().getString(
com.android.internal.R.string.status_bar_microphone));
+ if (mPermissionsHubEnabled) {
+ ignored.add(mContext.getResources().getString(
+ com.android.internal.R.string.status_bar_location));
+ }
return ignored;
}
@@ -256,6 +299,20 @@ public class QuickStatusBarHeader extends RelativeLayout implements
}
}
+ private void setChipVisibility(boolean chipVisible) {
+ if (chipVisible && mPermissionsHubEnabled) {
+ mPrivacyChip.setVisibility(View.VISIBLE);
+ // Makes sure that the chip is logged as viewed at most once each time QS is opened
+ // mListening makes sure that the callback didn't return after the user closed QS
+ if (!mPrivacyChipLogged && mListening) {
+ mPrivacyChipLogged = true;
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
+ }
+ } else {
+ mPrivacyChip.setVisibility(View.GONE);
+ }
+ }
+
private boolean updateRingerStatus() {
boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -363,6 +420,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
+ updatePrivacyChipAlphaAnimator();
}
private void updateStatusIconAlphaAnimator() {
@@ -377,6 +435,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements
.build();
}
+ private void updatePrivacyChipAlphaAnimator() {
+ mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
+ .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
+ .build();
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -415,6 +479,10 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mHeaderTextContainerView.setVisibility(INVISIBLE);
}
}
+ if (mPrivacyChipAlphaAnimator != null) {
+ mPrivacyChip.setExpanded(expansionFraction > 0.5);
+ mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
if (expansionFraction < 1 && expansionFraction > 0.99) {
if (mHeaderQsPanel.switchTileLayout()) {
updateResources();
@@ -453,6 +521,31 @@ public class QuickStatusBarHeader extends RelativeLayout implements
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
+ if (padding == null) {
+ mSystemIconsView.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
+ } else {
+ mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
+
+ }
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ boolean cornerCutout = cornerCutoutPadding != null
+ && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
+ if (cutout != null) {
+ Rect topCutout = cutout.getBoundingRectTop();
+ if (topCutout.isEmpty() || cornerCutout) {
+ mHasTopCutout = false;
+ lp.width = 0;
+ mSpace.setVisibility(View.GONE);
+ } else {
+ mHasTopCutout = true;
+ lp.width = topCutout.width();
+ mSpace.setVisibility(View.VISIBLE);
+ }
+ }
+ mSpace.setLayoutParams(lp);
+ setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -513,10 +606,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mZenController.addCallback(this);
mAlarmController.addCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
+ // Get the most up to date info
+ mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+ mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+ mPrivacyItemController.removeCallback(mPICCallback);
+ mPrivacyChipLogged = false;
}
}
@@ -534,6 +632,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
+ } else if (v == mPrivacyChip) {
+ // If the privacy chip is visible, it means there were some indicators
+ Handler mUiHandler = new Handler(Looper.getMainLooper());
+ mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
+ mUiHandler.post(() -> {
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+ mHost.collapsePanels();
+ });
} else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b4849355..d03082e6b442 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void addCallback(OverviewProxyListener listener) {
- mConnectionCallbacks.add(listener);
+ if (!mConnectionCallbacks.contains(listener)) {
+ mConnectionCallbacks.add(listener);
+ }
listener.onConnectionChanged(mOverviewProxy != null);
listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
new file mode 100644
index 000000000000..3fd7f94514f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+
+/**
+ * Receiver to proxy the share or edit intent, used to clean up the notification and send
+ * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
+ */
+public class ActionProxyReceiver extends BroadcastReceiver {
+ private static final String TAG = "ActionProxyReceiver";
+
+ private static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
+ private final StatusBar mStatusBar;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ public ActionProxyReceiver(Optional<StatusBar> statusBar,
+ ActivityManagerWrapper activityManagerWrapper,
+ ScreenshotSmartActions screenshotSmartActions) {
+ mStatusBar = statusBar.orElse(null);
+ mActivityManagerWrapper = activityManagerWrapper;
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, final Intent intent) {
+ Runnable startActivityRunnable = () -> {
+ try {
+ mActivityManagerWrapper.closeSystemWindows(
+ SYSTEM_DIALOG_REASON_SCREENSHOT).get(
+ CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Unable to share screenshot", e);
+ return;
+ }
+
+ PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setDisallowEnterPictureInPictureWhileLaunching(
+ intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+ try {
+ actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ };
+
+ if (mStatusBar != null) {
+ mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ true /* dismissShade */, true /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ startActivityRunnable.run();
+ }
+
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
+ ? ACTION_TYPE_EDIT
+ : ACTION_TYPE_SHARE;
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
deleted file mode 100644
index 8c4865510ed1..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
+++ /dev/null
@@ -1,43 +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 com.android.systemui.screenshot;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.AsyncTask;
-
-/**
- * An AsyncTask that deletes an image from the media store in the background.
- */
-class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private Context mContext;
-
- DeleteImageInBackgroundTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Void doInBackground(Uri... params) {
- if (params.length != 1) return null;
-
- Uri screenshotUri = params[0];
- ContentResolver resolver = mContext.getContentResolver();
- resolver.delete(screenshotUri, null, null);
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
new file mode 100644
index 000000000000..9028bb57c8e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Removes the file at a provided URI.
+ */
+public class DeleteScreenshotReceiver extends BroadcastReceiver {
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+ private final Executor mBackgroundExecutor;
+
+ @Inject
+ public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
+ @Background Executor backgroundExecutor) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ mBackgroundExecutor = backgroundExecutor;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
+ return;
+ }
+
+ // And delete the image from the media store
+ final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
+ mBackgroundExecutor.execute(() -> {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.delete(uri, null, null);
+ });
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 4a4aa79aadfa..c53523032353 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -21,8 +21,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -30,13 +28,10 @@ import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -62,7 +57,6 @@ import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -87,23 +81,15 @@ import android.widget.Toast;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* Class for handling device screen shots
*/
@@ -192,6 +178,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private final UiEventLogger mUiEventLogger;
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final Display mDisplay;
@@ -247,9 +234,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
@Inject
public GlobalScreenshot(
Context context, @Main Resources resources,
+ ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController screenshotNotificationsController,
UiEventLogger uiEventLogger) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
@@ -504,7 +493,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- dismissScreenshot("back pressed", true);
+ dismissScreenshot("back pressed", false);
return true;
}
return false;
@@ -704,7 +693,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
mSaveInBgTask.execute();
}
@@ -1024,6 +1013,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
mScreenshotPreview.setContentDescription(
mContext.getResources().getString(R.string.screenshot_preview_description));
+ mScreenshotPreview.setOnClickListener(null);
mScreenshotLayout.setAlpha(1);
mDismissButton.setTranslationY(0);
mActionsContainer.setTranslationY(0);
@@ -1116,119 +1106,4 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return insetDrawable;
}
}
-
- /**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
- public static class ActionProxyReceiver extends BroadcastReceiver {
- static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
- private final StatusBar mStatusBar;
-
- @Inject
- public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
- Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
- mStatusBar = statusBar != null ? statusBar.get() : null;
- }
-
- @Override
- public void onReceive(Context context, final Intent intent) {
- Runnable startActivityRunnable = () -> {
- try {
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- SYSTEM_DIALOG_REASON_SCREENSHOT).get(
- CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException | InterruptedException | ExecutionException e) {
- Slog.e(TAG, "Unable to share screenshot", e);
- return;
- }
-
- PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(
- intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- try {
- actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- };
-
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- startActivityRunnable.run();
- }
-
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
- ? ACTION_TYPE_EDIT
- : ACTION_TYPE_SHARE;
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, false);
- }
- }
- }
-
- /**
- * Removes the notification for a screenshot after a share target is chosen.
- */
- public static class TargetChosenReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Clear the notification only after the user has chosen a share action
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
- }
- }
-
- /**
- * Removes the last screenshot.
- */
- public static class DeleteScreenshotReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
- return;
- }
-
- // Clear the notification when the image is deleted
- ScreenshotNotificationsController.cancelScreenshotNotification(context);
-
- // And delete the image from the media store
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- new DeleteImageInBackgroundTask(context).execute(uri);
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
- }
- }
- }
-
- /**
- * Executes the smart action tapped by the user in the notification.
- */
- public static class SmartActionsReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
- Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
- ActivityOptions opts = ActivityOptions.makeBasic();
-
- try {
- pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- ScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, true);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 468b9b16addb..df1d78953f46 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -81,6 +81,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
private final Context mContext;
+ private final ScreenshotSmartActions mScreenshotSmartActions;
private final GlobalScreenshot.SaveImageInBackgroundData mParams;
private final GlobalScreenshot.SavedImageData mImageData;
private final String mImageFileName;
@@ -90,8 +91,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final boolean mSmartActionsEnabled;
private final Random mRandom = new Random();
- SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
+ SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
+ GlobalScreenshot.SaveImageInBackgroundData data) {
mContext = context;
+ mScreenshotSmartActions = screenshotSmartActions;
mImageData = new GlobalScreenshot.SavedImageData();
// Prepare all the output metadata
@@ -141,7 +144,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
mScreenshotId, uri, image, mSmartActionsProvider,
mSmartActionsEnabled, getUserHandle(mContext));
@@ -199,7 +202,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
1000);
smartActions.addAll(buildSmartActions(
- ScreenshotSmartActions.getSmartActions(
+ mScreenshotSmartActions.getSmartActions(
mScreenshotId, smartActionsFuture, timeoutMs,
mSmartActionsProvider),
mContext));
@@ -274,11 +277,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// by setting the (otherwise unused) request code to the current user id.
int requestCode = context.getUserId();
- PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
Intent sharingChooserIntent =
- Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
+ Intent.createChooser(sharingIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -288,7 +288,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
.putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
@@ -333,10 +333,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a edit action
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ new Intent(context, ActionProxyReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
- .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
- editIntent.getComponent() != null)
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
@@ -358,7 +356,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Create a delete action for the notification
PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ new Intent(context, DeleteScreenshotReceiver.class)
.putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
@@ -398,7 +396,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
String actionType = extras.getString(
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
+ Intent intent = new Intent(context, SmartActionsReceiver.class)
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 442b373b31be..633cdd6ca5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -39,14 +39,21 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Collects the static functions for retrieving and acting on smart actions.
*/
+@Singleton
public class ScreenshotSmartActions {
private static final String TAG = "ScreenshotSmartActions";
+ @Inject
+ public ScreenshotSmartActions() {}
+
@VisibleForTesting
- static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
+ CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
String screenshotId, Uri screenshotUri, Bitmap image,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
boolean smartActionsEnabled, UserHandle userHandle) {
@@ -86,7 +93,7 @@ public class ScreenshotSmartActions {
}
@VisibleForTesting
- static List<Notification.Action> getSmartActions(String screenshotId,
+ List<Notification.Action> getSmartActions(String screenshotId,
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
long startTimeMs = SystemClock.uptimeMillis();
@@ -116,7 +123,7 @@ public class ScreenshotSmartActions {
}
}
- static void notifyScreenshotOp(String screenshotId,
+ void notifyScreenshotOp(String screenshotId,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
@@ -127,7 +134,7 @@ public class ScreenshotSmartActions {
}
}
- static void notifyScreenshotAction(Context context, String screenshotId, String action,
+ void notifyScreenshotAction(Context context, String screenshotId, String action,
boolean isSmartAction) {
try {
ScreenshotNotificationSmartActionsProvider provider =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
new file mode 100644
index 000000000000..217235b16ecf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Slog;
+
+import javax.inject.Inject;
+
+
+/**
+ * Executes the smart action tapped by the user in the notification.
+ */
+public class SmartActionsReceiver extends BroadcastReceiver {
+ private static final String TAG = "SmartActionsReceiver";
+
+ private final ScreenshotSmartActions mScreenshotSmartActions;
+
+ @Inject
+ SmartActionsReceiver(ScreenshotSmartActions screenshotSmartActions) {
+ mScreenshotSmartActions = screenshotSmartActions;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
+ Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+ ActivityOptions opts = ActivityOptions.makeBasic();
+
+ try {
+ pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled", e);
+ }
+
+ mScreenshotSmartActions.notifyScreenshotAction(
+ context, intent.getStringExtra(EXTRA_ID), actionType, true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 6f143da5405a..a043f0f1e50c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -61,7 +61,7 @@ public class TakeScreenshotService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
- mScreenshot.dismissScreenshot("close system dialogs", true);
+ mScreenshot.dismissScreenshot("close system dialogs", false);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 570a4bb3cd12..eb7231211ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,16 +39,16 @@ import android.window.WindowOrganizer;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.TransactionPool;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.DisplayLayout;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 5aeca5e07bdd..84ec38744e98 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -33,8 +33,8 @@ import android.window.WindowOrganizer;
import androidx.annotation.Nullable;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.wm.DisplayImeController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.TransactionPool;
class DividerImeController implements DisplayImeController.ImePositionProcessor {
private static final String TAG = "DividerImeController";
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 3b7f3152ec76..c24431c22d62 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -19,13 +19,13 @@ package com.android.systemui.stackdivider;
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 6ea3132ac942..d869333e11a7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -32,7 +32,7 @@ import android.os.Binder;
import android.view.View;
import android.view.WindowManager;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.SystemWindows;
/**
* Manages the window parameters of the docked stack divider.
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
index 69095f7538c5..a34e85517953 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -34,7 +34,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayLayout;
/**
* Handles split-screen related internal display layout. In general, this represents the
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
index 1ff404677ea6..6812f62422a7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
@@ -25,7 +25,7 @@ import android.window.WindowOrganizer;
import androidx.annotation.NonNull;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 410e3dd39a0b..2b3681281064 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -40,7 +40,7 @@ import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c540e743..2638d28733e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@ public class NavigationBarController implements Callbacks {
Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreAppearanceAndTransientState();
- mNavigationBars.append(displayId, navBar);
+ mNavigationBars.put(displayId, navBar);
if (result != null) {
navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 9abc66056452..aba9e1005559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,18 +24,24 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -52,6 +58,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
@@ -127,6 +134,8 @@ public class NotificationEntryManager implements
private final NotificationEntryManagerLogger mLogger;
+ private final IStatusBarService mStatusBarService;
+
// Lazily retrieved dependencies
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -138,6 +147,8 @@ public class NotificationEntryManager implements
private final NotificationRankingManager mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
+ private final HeadsUpManager mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -201,7 +212,10 @@ public class NotificationEntryManager implements
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController
+ ) {
mLogger = logger;
mGroupManager = groupManager;
mRankingManager = rankingManager;
@@ -211,6 +225,11 @@ public class NotificationEntryManager implements
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -496,6 +515,9 @@ public class NotificationEntryManager implements
removedByUser |= entryDismissed;
mLogger.logNotifRemoved(entry.getKey(), removedByUser);
+ if (removedByUser && visibility != null) {
+ sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
+ }
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser, reason);
}
@@ -511,6 +533,36 @@ public class NotificationEntryManager implements
}
}
+ private void sendNotificationRemovalToServer(
+ String key,
+ StatusBarNotification notification,
+ NotificationVisibility nv) {
+ final String pkg = notification.getPackageName();
+ final String tag = notification.getTag();
+ final int id = notification.getId();
+ final int userId = notification.getUser().getIdentifier();
+ try {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (mHeadsUpManager.isAlerting(key)) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStatusBarStateController.isDozing()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+ int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+ mStatusBarService.onNotificationClear(
+ pkg,
+ tag,
+ id,
+ userId,
+ notification.getKey(),
+ dismissalSurface,
+ dismissalSentiment,
+ nv);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ }
+
/**
* Ensures that the group children are cancelled immediately when the group summary is cancelled
* instead of waiting for the notification manager to send all cancels. Otherwise this could
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 27476964b9af..f982cf05de97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -382,7 +382,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
private fun shouldAnimateVisibility() =
- dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking()
+ dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
interface WakeUpListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d2c202cb485f..d661b5e2b7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import java.util.concurrent.Executor;
@@ -88,7 +89,9 @@ public interface NotificationsModule {
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController) {
+ ForegroundServiceDismissalFeatureController fgsFeatureController,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController) {
return new NotificationEntryManager(
logger,
groupManager,
@@ -98,7 +101,9 @@ public interface NotificationsModule {
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
- fgsFeatureController);
+ fgsFeatureController,
+ headsUpManager,
+ statusBarStateController);
}
/** Provides an instance of {@link NotificationGutsManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index bd0d0b31e4dc..4441270f895b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -21,7 +21,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,7 +43,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Collection;
import java.util.Collections;
@@ -74,7 +72,6 @@ public class NotificationLogger implements StateListener {
private final Executor mUiBgExecutor;
private final NotificationEntryManager mEntryManager;
private final NotificationPanelLogger mNotificationPanelLogger;
- private HeadsUpManager mHeadsUpManager;
private final ExpansionStateLogger mExpansionStateLogger;
protected Handler mHandler = new Handler();
@@ -226,9 +223,6 @@ public class NotificationLogger implements StateListener {
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
- if (removedByUser && visibility != null) {
- logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
- }
mExpansionStateLogger.onEntryRemoved(entry.getKey());
}
@@ -250,10 +244,6 @@ public class NotificationLogger implements StateListener {
mListContainer = listContainer;
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
public void stopNotificationLogging() {
if (mLogging) {
mLogging = false;
@@ -296,30 +286,6 @@ public class NotificationLogger implements StateListener {
}
}
- // TODO: This method has side effects, it is NOT just logging that a notification
- // was cleared, it also actually removes the notification
- private void logNotificationClear(String key, StatusBarNotification notification,
- NotificationVisibility nv) {
- final String pkg = notification.getPackageName();
- final String tag = notification.getTag();
- final int id = notification.getId();
- final int userId = notification.getUserId();
- try {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(key)) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mListContainer.hasPulsingNotifications()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
- int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
- mBarService.onNotificationClear(pkg, tag, id, userId, notification.getKey(),
- dismissalSurface,
- dismissalSentiment, nv);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- }
-
/**
* Logs Notification inflation error
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index f5ea1c880a41..5fab4bea9a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -28,6 +28,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
@@ -52,6 +53,7 @@ public class DozeParameters implements TunerService.Tunable,
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
+ private final BatteryController mBatteryController;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
@@ -62,10 +64,12 @@ public class DozeParameters implements TunerService.Tunable,
AmbientDisplayConfiguration ambientDisplayConfiguration,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
PowerManager powerManager,
+ BatteryController batteryController,
TunerService tunerService) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
+ mBatteryController = batteryController;
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
@@ -164,7 +168,7 @@ public class DozeParameters implements TunerService.Tunable,
* @return {@code true} if enabled and available.
*/
public boolean getAlwaysOn() {
- return mDozeAlwaysOn;
+ return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index a35aca553c4f..9606318e1992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
+ private final SysUiState mSysUiState;
private final Runnable mStateChangeCallback;
private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
};
+ private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+ new SysUiState.SysUiStateCallback() {
+ @Override
+ public void onSystemUiStateChanged(int sysUiFlags) {
+ mSysUiFlags = sysUiFlags;
+ }
+ };
+
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiFlagContainer, PluginManager pluginManager,
- Runnable stateChangeCallback) {
+ SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
super(Dependency.get(BroadcastDispatcher.class));
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
mOverviewProxyService = overviewProxyService;
+ mSysUiState = sysUiState;
mPluginManager = pluginManager;
mStateChangeCallback = stateChangeCallback;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -238,7 +247,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
updateCurrentUserResources();
- sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources() {
@@ -287,6 +295,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mIsAttached = true;
Dependency.get(ProtoTracer.class).add(this);
mOverviewProxyService.addCallback(mQuickSwitchListener);
+ mSysUiState.addCallback(mSysUiStateCallback);
updateIsEnabled();
startTracking();
}
@@ -298,6 +307,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mIsAttached = false;
Dependency.get(ProtoTracer.class).remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
+ mSysUiState.removeCallback(mSysUiStateCallback);
updateIsEnabled();
stopTracking();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b7733cc5acd7..f43fa648a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@ import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@ import com.android.systemui.util.LifecycleFragment;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -354,6 +356,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
// If the button will actually become visible and the navbar is about to hide,
// tell the statusbar to keep it around for longer
mAutoHideController.touchAutoHide();
+ mNavigationBarView.notifyActiveTouchRegions();
}
};
@@ -550,6 +553,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
+ mHandler.removeCallbacks(mAutoDim);
+ mNavigationBarView = null;
+ mOrientationHandle = null;
}
@Override
@@ -1458,11 +1464,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
- final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
- .create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ final NavigationBarFragment fragment =
+ FragmentHostManager.get(v).create(NavigationBarFragment.class);
final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1472,6 +1478,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
@Override
public void onViewDetachedFromWindow(View v) {
+ final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+ fragmentHost.removeTagListener(TAG, listener);
FragmentHostManager.removeAndDestroy(v);
navigationBarView.removeOnAttachStateChangeListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7936e533f76d..84512ac85fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -115,12 +115,8 @@ public class NavigationBarView extends FrameLayout implements
int mNavigationIconHints = 0;
private int mNavBarMode;
- private Rect mHomeButtonBounds = new Rect();
- private Rect mBackButtonBounds = new Rect();
- private Rect mRecentsButtonBounds = new Rect();
- private Rect mRotationButtonBounds = new Rect();
private final Region mActiveRegion = new Region();
- private int[] mTmpPosition = new int[2];
+ private Rect mTmpBounds = new Rect();
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
@@ -712,6 +708,7 @@ public class NavigationBarView extends FrameLayout implements
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+ notifyActiveTouchRegions();
}
@VisibleForTesting
@@ -929,42 +926,30 @@ public class NavigationBarView extends FrameLayout implements
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ notifyActiveTouchRegions();
+ mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
+ }
+
+ /**
+ * Notifies the overview service of the active touch regions.
+ */
+ public void notifyActiveTouchRegions() {
mActiveRegion.setEmpty();
- updateButtonLocation(getBackButton(), mBackButtonBounds, true);
- updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
- updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
- updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
- // TODO: Handle button visibility changes
+ updateButtonLocation(getBackButton());
+ updateButtonLocation(getHomeButton());
+ updateButtonLocation(getRecentsButton());
+ updateButtonLocation(getRotateSuggestionButton());
mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
- mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
}
- private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
- boolean isActive) {
+ private void updateButtonLocation(ButtonDispatcher button) {
View view = button.getCurrentView();
- if (view == null) {
- buttonBounds.setEmpty();
+ if (view == null || !button.isVisible()) {
return;
}
- // Temporarily reset the translation back to origin to get the position in window
- final float posX = view.getTranslationX();
- final float posY = view.getTranslationY();
- view.setTranslationX(0);
- view.setTranslationY(0);
-
- if (isActive) {
- view.getLocationOnScreen(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- mActiveRegion.op(buttonBounds, Op.UNION);
- }
- view.getLocationInWindow(mTmpPosition);
- buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getMeasuredWidth(),
- mTmpPosition[1] + view.getMeasuredHeight());
- view.setTranslationX(posX);
- view.setTranslationY(posY);
+
+ view.getBoundsOnScreen(mTmpBounds);
+ mActiveRegion.op(mTmpBounds, Op.UNION);
}
private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5bb8fab8a62e..b2cfceae2cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -47,6 +47,9 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.screenrecord.RecordingController;
@@ -70,6 +73,9 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.time.DateFormatUtil;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
@@ -87,13 +93,13 @@ public class PhoneStatusBarPolicy
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
+ PrivacyItemController.Callback,
LocationController.LocationChangeCallback,
RecordingController.RecordingStateChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final int LOCATION_STATUS_ICON_ID =
- com.android.internal.R.drawable.perm_group_location;
+ static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
private final String mSlotCast;
private final String mSlotHotspot;
@@ -107,6 +113,8 @@ public class PhoneStatusBarPolicy
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
+ private final String mSlotMicrophone;
+ private final String mSlotCamera;
private final String mSlotSensorsOff;
private final String mSlotScreenRecord;
private final int mDisplayId;
@@ -132,6 +140,7 @@ public class PhoneStatusBarPolicy
private final DeviceProvisionedController mProvisionedController;
private final KeyguardStateController mKeyguardStateController;
private final LocationController mLocationController;
+ private final PrivacyItemController mPrivacyItemController;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
@@ -162,7 +171,8 @@ public class PhoneStatusBarPolicy
RecordingController recordingController,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
- RingerModeTracker ringerModeTracker) {
+ RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController) {
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
@@ -181,6 +191,7 @@ public class PhoneStatusBarPolicy
mProvisionedController = deviceProvisionedController;
mKeyguardStateController = keyguardStateController;
mLocationController = locationController;
+ mPrivacyItemController = privacyItemController;
mSensorPrivacyController = sensorPrivacyController;
mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
@@ -200,6 +211,8 @@ public class PhoneStatusBarPolicy
mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+ mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone);
+ mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
mSlotScreenRecord = resources.getString(
com.android.internal.R.string.status_bar_screen_record);
@@ -271,6 +284,13 @@ public class PhoneStatusBarPolicy
mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
+ // privacy items
+ mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
+ mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()));
+ mIconController.setIconVisibility(mSlotMicrophone, false);
+ mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
+ mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()));
+ mIconController.setIconVisibility(mSlotCamera, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
@@ -294,6 +314,7 @@ public class PhoneStatusBarPolicy
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardStateController.addCallback(this);
+ mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
@@ -609,9 +630,44 @@ public class PhoneStatusBarPolicy
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
+ @Override // PrivacyItemController.Callback
+ public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ updatePrivacyItems(privacyItems);
+ }
+
+ private void updatePrivacyItems(List<PrivacyItem> items) {
+ boolean showCamera = false;
+ boolean showMicrophone = false;
+ boolean showLocation = false;
+ for (PrivacyItem item : items) {
+ if (item == null /* b/124234367 */) {
+ Log.e(TAG, "updatePrivacyItems - null item found");
+ StringWriter out = new StringWriter();
+ mPrivacyItemController.dump(null, new PrintWriter(out), null);
+ // Throw so we can look into this
+ throw new NullPointerException(out.toString());
+ }
+ switch (item.getPrivacyType()) {
+ case TYPE_CAMERA:
+ showCamera = true;
+ break;
+ case TYPE_LOCATION:
+ showLocation = true;
+ break;
+ case TYPE_MICROPHONE:
+ showMicrophone = true;
+ break;
+ }
+ }
+
+ mIconController.setIconVisibility(mSlotCamera, showCamera);
+ mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+ mIconController.setIconVisibility(mSlotLocation, showLocation);
+ }
+
@Override
public void onLocationActiveChanged(boolean active) {
- updateLocation();
+ if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
}
// Updates the status view based on the current state of location requests.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c5acd9bd0f06..c5571e8ceb20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,9 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_CLOCK;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -1101,7 +1098,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -4175,7 +4171,6 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
mTopHidesStatusBar = topAppHidesStatusBar;
- updateStatusBarIcons(topAppHidesStatusBar);
if (!topAppHidesStatusBar && mWereIconsJustHidden) {
// Immediately update the icon hidden state, since that should only apply if we're
// staying fullscreen.
@@ -4185,17 +4180,6 @@ public class StatusBar extends SystemUI implements DemoMode,
updateHideIconsForBouncer(true /* animate */);
}
- private void updateStatusBarIcons(boolean topAppHidesStatusBar) {
- int flags1 = StatusBarManager.DISABLE_NONE;
- int flags2 = StatusBarManager.DISABLE2_NONE;
- if (topAppHidesStatusBar) {
- flags1 = DISABLE_NOTIFICATION_ICONS | DISABLE_CLOCK;
- flags2 = DISABLE2_SYSTEM_ICONS;
- }
-
- mCommandQueue.disable(mDisplayId, flags1, flags2, false);
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 6dd96f92b344..95601955ec04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.icu.text.DateTimePatternGenerator;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -53,8 +54,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
@@ -391,15 +390,16 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
private final CharSequence getSmallTime() {
Context context = getContext();
boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+ DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+ context.getResources().getConfiguration().locale);
final char MAGIC1 = '\uEF00';
final char MAGIC2 = '\uEF01';
SimpleDateFormat sdf;
String format = mShowSeconds
- ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
- : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
+ ? is24 ? dtpg.getBestPattern("Hms") : dtpg.getBestPattern("hms")
+ : is24 ? dtpg.getBestPattern("Hm") : dtpg.getBestPattern("hm");
if (!format.equals(mClockFormatString)) {
mContentDescriptionFormat = new SimpleDateFormat(format);
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 251693e162d0..adfc14e1d72b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,7 @@ import com.android.systemui.BootCompleteCache;
import com.android.systemui.appops.AppOpItem;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.Utils;
@@ -65,8 +66,8 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
@Inject
public LocationControllerImpl(Context context, AppOpsController appOpsController,
- @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher,
- BootCompleteCache bootCompleteCache) {
+ @Main Looper mainLooper, @Background Handler backgroundHandler,
+ BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) {
mContext = context;
mAppOpsController = appOpsController;
mBootCompleteCache = bootCompleteCache;
@@ -80,7 +81,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this);
// Examine the current location state and initialize the status view.
- updateActiveLocationRequests();
+ backgroundHandler.post(this::updateActiveLocationRequests);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af770298..b1241b160d70 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@ public class UsbDebuggingActivity extends AlertActivity
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
}
Intent intent = getIntent();
@@ -119,6 +121,7 @@ public class UsbDebuggingActivity extends AlertActivity
}
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
if (!connected) {
+ Log.d(TAG, "USB disconnected, notifying service");
notifyService(false);
mActivity.finish();
}
@@ -126,29 +129,20 @@ public class UsbDebuggingActivity extends AlertActivity
}
@Override
- public void onStart() {
- super.onStart();
- if (mDisconnectedReceiver != null) {
- IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
- }
- }
-
- @Override
- protected void onStop() {
+ protected void onDestroy() {
if (mDisconnectedReceiver != null) {
mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
}
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- // If the ADB service has not yet been notified due to this dialog being closed in some
- // other way then notify the service to deny the connection to ensure system_server sends
- // a response to adbd.
- if (!mServiceNotified) {
- notifyService(false);
+ // Only notify the service if the activity is finishing; if onDestroy has been called due to
+ // a configuration change then allow the user to still authorize the connection the next
+ // time the activity is in the foreground.
+ if (isFinishing()) {
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server
+ // sends a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
}
super.onDestroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
new file mode 100644
index 000000000000..84ab66b66a7c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+/**
+ * Public interface that can be injected to interact with Settings.Global.
+ *
+ * See {@link SettingsProxy} for details.
+ */
+public interface GlobalSettings extends SettingsProxy {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
new file mode 100644
index 000000000000..1a30b0a8d8bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class GlobalSettingsImpl implements GlobalSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ GlobalSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Global.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Global.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.System and Settings.Secure");
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Global.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
new file mode 100644
index 000000000000..798033e841d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+/**
+ * Public interface that can be injected to interact with Settings.Secure.
+ *
+ * See {@link SettingsProxy} for details.
+ */
+
+public interface SecureSettings extends SettingsProxy {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
new file mode 100644
index 000000000000..020c234191e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SecureSettingsImpl implements SecureSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SecureSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.Secure.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.Secure.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ return Settings.Secure.putStringForUser(
+ mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
new file mode 100644
index 000000000000..5c37f797b678
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
+ * constructors and then faked or mocked as needed in tests.
+ *
+ * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
+ * injected as needed.
+ *
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface SettingsProxy {
+
+ /**
+ * Returns the {@link ContentResolver} this instance was constructed with.
+ */
+ ContentResolver getContentResolver();
+
+ /**
+ * Returns the user id for the associated {@link ContentResolver}.
+ */
+ default int getUserId() {
+ return getContentResolver().getUserId();
+ }
+
+ /**
+ * Construct the content URI for a particular name/value pair,
+ * useful for monitoring changes with a ContentObserver.
+ * @param name to look up in the table
+ * @return the corresponding content URI, or null if not present
+ */
+ Uri getUriFor(String name);
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserver(String name, ContentObserver settingsObserver) {
+ registerContentObserverForUser(name, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, ContentObserver settingsObserver, int userHandle) {
+ getContentResolver().registerContentObserver(
+ getUriFor(name), false, settingsObserver, userHandle);
+ }
+
+ /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
+ default void unregisterContentObserver(ContentObserver settingsObserver) {
+ getContentResolver().unregisterContentObserver(settingsObserver);
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ default String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ /**See {@link #getString(String)}. */
+ String getStringForUser(String name, int userHandle);
+
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ boolean putString(String name, String value, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putString(String name, String value) {
+ return putStringForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(String name, String value, int userHandle);
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+ /**
+ * Store a name/value pair into the database.
+ * <p>
+ * The method takes an optional tag to associate with the setting
+ * which can be used to clear only settings made by your package and
+ * associated with this tag by passing the tag to {@link
+ * #resetToDefaults(String)}. Anyone can override
+ * the current tag. Also if another package changes the setting
+ * then the tag will be set to the one specified in the set call
+ * which can be null. Also any of the settings setters that do not
+ * take a tag as an argument effectively clears the tag.
+ * </p><p>
+ * For example, if you set settings A and B with tags T1 and T2 and
+ * another app changes setting A (potentially to the same value), it
+ * can assign to it a tag T3 (note that now the package that changed
+ * the setting is not yours). Now if you reset your changes for T1 and
+ * T2 only setting B will be reset and A not (as it was changed by
+ * another package) but since A did not change you are in the desired
+ * initial state. Now if the other app changes the value of A (assuming
+ * you registered an observer in the beginning) you would detect that
+ * the setting was changed by another app and handle this appropriately
+ * (ignore, set back to some value, etc).
+ * </p><p>
+ * Also the method takes an argument whether to make the value the
+ * default for this setting. If the system already specified a default
+ * value, then the one passed in here will <strong>not</strong>
+ * be set as the default.
+ * </p>
+ *
+ * @param name to store.
+ * @param value to associate with the name.
+ * @param tag to associate with the setting.
+ * @param makeDefault whether to make the value the default one.
+ * @return true if the value was set, false on database errors.
+ *
+ * @see #resetToDefaults(String)
+ *
+ */
+ boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault);
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you. The default value will be returned if the setting is
+ * not defined or not an integer.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid integer.
+ */
+ default int getInt(String name, int def) {
+ return getIntForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getInt(String, int)}. */
+ default int getIntForUser(String name, int def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ *
+ * @return The setting's current value.
+ */
+ default int getInt(String name) throws Settings.SettingNotFoundException {
+ return getIntForUser(name, getUserId());
+ }
+
+ /** See {@link #getInt(String)}. */
+ default int getIntForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as an
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putInt(String name, int value) {
+ return putIntForUser(name, value, getUserId());
+ }
+ /** See {@link #putInt(String, int)}. */
+ default boolean putIntForUser(String name, int value, int userHandle) {
+ return putStringForUser(name, Integer.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you. The default value will be returned if the setting is
+ * not defined or not a {@code long}.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid {@code long}.
+ */
+ default long getLong(String name, long def) {
+ return getLongForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getLong(String, long)}. */
+ default long getLongForUser(String name, long def, int userHandle) {
+ String valString = getStringForUser(name, userHandle);
+ long value;
+ try {
+ value = valString != null ? Long.parseLong(valString) : def;
+ } catch (NumberFormatException e) {
+ value = def;
+ }
+ return value;
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @return The setting's current value.
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ */
+ default long getLong(String name) throws Settings.SettingNotFoundException {
+ return getLongForUser(name, getUserId());
+ }
+
+ /** See {@link #getLong(String)}. */
+ default long getLongForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String valString = getStringForUser(name, userHandle);
+ try {
+ return Long.parseLong(valString);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a secure settings value as a long
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putLong(String name, long value) {
+ return putLongForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putLong(String, long)}. */
+ default boolean putLongForUser(String name, long value, int userHandle) {
+ return putStringForUser(name, Long.toString(value), userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a floating point number. Note that internally setting values are
+ * always stored as strings; this function converts the string to an
+ * float for you. The default value will be returned if the setting
+ * is not defined or not a valid float.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid float.
+ */
+ default float getFloat(String name, float def) {
+ return getFloatForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getFloat(String)}. */
+ default float getFloatForUser(String name, float def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Float.parseFloat(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a float. Note that internally setting values are always
+ * stored as strings; this function converts the string to a float
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not a float.
+ *
+ * @return The setting's current value.
+ */
+ default float getFloat(String name) throws Settings.SettingNotFoundException {
+ return getFloatForUser(name, getUserId());
+ }
+
+ /** See {@link #getFloat(String, float)}. */
+ default float getFloatForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ if (v == null) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ try {
+ return Float.parseFloat(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * floating point number. This will either create a new entry in the
+ * table if the given name does not exist, or modify the value of the
+ * existing row with that name. Note that internally setting values
+ * are always stored as strings, so this function converts the given
+ * value to a string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putFloat(String name, float value) {
+ return putFloatForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putFloat(String, float)} */
+ default boolean putFloatForUser(String name, float value, int userHandle) {
+ return putStringForUser(name, Float.toString(value), userHandle);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
new file mode 100644
index 000000000000..f36c335e0f44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module for classes within com.android.systemui.util.settings.
+ */
+@Module
+public interface SettingsUtilModule {
+
+ /** Bind SecureSettingsImpl to SecureSettings. */
+ @Binds
+ SecureSettings bindsSecureSettings(SecureSettingsImpl impl);
+
+ /** Bind SystemSettingsImpl to SystemSettings. */
+ @Binds
+ SystemSettings bindsSystemSettings(SystemSettingsImpl impl);
+
+ /** Bind GlobalSettingsImpl to GlobalSettings. */
+ @Binds
+ GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
new file mode 100644
index 000000000000..d57d7496381c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+/**
+ * Public interface that can be injected to interact with Settings.System.
+ *
+ * See {@link SettingsProxy} for details.
+ */
+public interface SystemSettings extends SettingsProxy {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
new file mode 100644
index 000000000000..0dbb76f8f758
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SystemSettingsImpl implements SystemSettings {
+ private final ContentResolver mContentResolver;
+
+ @Inject
+ SystemSettingsImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Settings.System.getUriFor(name);
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return Settings.System.getStringForUser(mContentResolver, name, userHandle);
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return Settings.System.putString(mContentResolver, name, value, overrideableByRestore);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return Settings.System.putStringForUser(mContentResolver, name, value, userHandle);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ throw new UnsupportedOperationException(
+ "This method only exists publicly for Settings.Secure and Settings.Global");
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
new file mode 100644
index 000000000000..fbc167683a2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wmshell;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell}.
+ */
+@Module
+// TODO(b/161116823) Clean up dependencies after wm shell migration finished.
+public class WindowManagerShellModule {
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController provideDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static DisplayImeController provideDisplayImeController(
+ IWindowManager wmService, DisplayController displayController,
+ @Main Handler mainHandler, TransactionPool transactionPool) {
+ return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 000000000000..9cb4fb319fa2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+ private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+ mContext.getSystemService(DisplayManager.class)) {
+
+ @NonNull
+ @Override
+ protected Object createInstance(Display display) {
+ return new Object();
+ }
+ };
+ }
+
+ @Test
+ public void get_instanceIsNotNull() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ assertNotNull(object);
+ }
+
+ @Test
+ public void get_removeExistedObject_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+
+ @Test
+ public void get_clearAllObjects_newObject() {
+ Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+ mDisplayIdIndexSupplier.clear();
+
+ Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+ assertNotEquals(object, newObject);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd9610a..69482791ef23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
package com.android.systemui.accessibility;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -38,45 +31,43 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
/** Tests the ModeSwitchesController. */
public class ModeSwitchesControllerTest extends SysuiTestCase {
- private WindowManager mWindowManager;
+ @Mock
+ private ModeSwitchesController.SwitchSupplier mSupplier;
+ @Mock
+ private MagnificationModeSwitch mModeSwitch;
private ModeSwitchesController mModeSwitchesController;
+
@Before
public void setUp() {
- mWindowManager = mock(WindowManager.class);
- Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
- when(mWindowManager.getDefaultDisplay()).thenReturn(display);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
- when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mModeSwitchesController = new ModeSwitchesController(mContext);
+ MockitoAnnotations.initMocks(this);
+ when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+ mModeSwitchesController = new ModeSwitchesController(mSupplier);
}
@Test
public void testShowButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mWindowManager).addView(any(), any());
+
+ verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
public void testRemoveButton() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
- verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
- verify(mWindowManager).removeView(eq(captor.getValue()));
+ verify(mModeSwitch).removeButton();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index e0049d1349f1..4fdc06e64e2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -26,11 +26,14 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
@@ -56,6 +59,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
+ private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@Mock
private AppOpsManager mAppOpsManager;
@@ -65,6 +69,10 @@ public class AppOpsControllerTest extends SysuiTestCase {
private AppOpsControllerImpl.H mMockHandler;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private PermissionFlagsCache mFlagsCache;
+ @Mock
+ private PackageManager mPackageManager;
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
@@ -76,8 +84,22 @@ public class AppOpsControllerTest extends SysuiTestCase {
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
- mController =
- new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpManager);
+ // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
+ // TEST_UID_NON_USER_SENSITIVE are user sensitive.
+ getContext().setMockPackageManager(mPackageManager);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
+ PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
+ .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+ when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
+ eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
+
+ mController = new AppOpsControllerImpl(
+ mContext,
+ mTestableLooper.getLooper(),
+ mDumpManager,
+ mFlagsCache
+ );
}
@Test
@@ -173,6 +195,26 @@ public class AppOpsControllerTest extends SysuiTestCase {
}
@Test
+ public void nonUserSensitiveOpsAreIgnored() {
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+ assertEquals(0, mController.getActiveAppOpsForUser(
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ }
+
+ @Test
+ public void nonUserSensitiveOpsNotNotified() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never())
+ .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
+ }
+
+ @Test
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
new file mode 100644
index 000000000000..0fb0ce087ee3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PermissionFlagsCacheTest : SysuiTestCase() {
+
+ companion object {
+ const val TEST_PERMISSION = "test_permission"
+ const val TEST_PACKAGE = "test_package"
+ const val TEST_UID1 = 1000
+ const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
+ }
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var flagsCache: PermissionFlagsCache
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ executor = FakeExecutor(FakeSystemClock())
+
+ flagsCache = PermissionFlagsCache(packageManager, executor)
+ executor.runAllReady()
+ }
+
+ @Test
+ fun testNotListeningByDefault() {
+ verify(packageManager, never()).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testGetCorrectFlags() {
+ `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
+ }
+
+ @Test
+ fun testFlagIsCached() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager, times(1)).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1)
+ )
+ }
+
+ @Test
+ fun testListeningAfterFirstRequest() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ verify(packageManager).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testListeningOnlyOnce() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
+
+ verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
+ }
+
+ @Test
+ fun testUpdateFlag() {
+ assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+
+ `when`(packageManager.getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID1))
+ ).thenReturn(1)
+
+ flagsCache.onPermissionsChanged(TEST_UID1)
+
+ executor.runAllReady()
+
+ assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+ }
+
+ @Test
+ fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
+ flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+ flagsCache.onPermissionsChanged(TEST_UID2)
+
+ executor.runAllReady()
+
+ verify(packageManager, never()).getPermissionFlags(
+ TEST_PERMISSION,
+ TEST_PACKAGE,
+ UserHandle.getUserHandleForUid(TEST_UID2)
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c738f966..d4a94c5b9e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@ import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@ public class AuthControllerTest extends SysuiTestCase {
IActivityTaskManager getActivityTaskManager() {
return mock(IActivityTaskManager.class);
}
+
+ @Override
+ FingerprintManager getFingerprintManager(Context context) {
+ return mock(FingerprintManager.class);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 7ebead8a33fa..4f0ff4242b6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -47,6 +47,7 @@ import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
@@ -84,6 +85,7 @@ public class DozeSensorsTest extends SysuiTestCase {
private DozeLog mDozeLog;
@Mock
private ProximitySensor mProximitySensor;
+ private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors;
@@ -154,7 +156,7 @@ public class DozeSensorsTest extends SysuiTestCase {
TestableDozeSensors() {
super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor);
+ mProximitySensor, mFakeSettings);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index d3af835873e2..1ed58714fb9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -47,6 +47,7 @@ import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.sensors.FakeThresholdSensor;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -99,7 +100,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
- mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher);
+ mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings());
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 84a261b6e7d2..3231b2852e7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -41,7 +41,7 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
index 180c4507bd09..3b284b14c36f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -33,7 +33,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.systemui.model.SysUiState;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index b6b2217837b2..55bec54eacb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -33,7 +33,7 @@ import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index 80fe0f095020..3a4ba6a213dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -31,7 +31,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.systemui.model.SysUiState;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index 70c2bba040a0..e9d2b73182e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -33,7 +33,7 @@ import android.view.Gravity;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
new file mode 100644
index 000000000000..dcee5a716ceb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PrivacyChipBuilderTest : SysuiTestCase() {
+
+ companion object {
+ val TEST_UID = 1
+ }
+
+ @Test
+ fun testGenerateAppsList() {
+ val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Bar", TEST_UID))
+ val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+ "Bar", TEST_UID))
+ val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
+ "Foo", TEST_UID))
+ val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+ "Baz", TEST_UID))
+
+ val items = listOf(bar2, foo0, baz1, bar3)
+
+ val textBuilder = PrivacyChipBuilder(context, items)
+
+ val list = textBuilder.appsAndTypes
+ assertEquals(3, list.size)
+ val appsList = list.map { it.first }
+ val typesList = list.map { it.second }
+ // List is sorted by number of types and then by types
+ assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+ assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+ assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+ assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
+ }
+
+ @Test
+ fun testOrder() {
+ // We want location to always go last, so it will go in the "+ other apps"
+ val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("Camera", TEST_UID))
+ val appMicrophone =
+ PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+ PrivacyApplication("Microphone", TEST_UID))
+ val appLocation =
+ PrivacyItem(PrivacyType.TYPE_LOCATION,
+ PrivacyApplication("Location", TEST_UID))
+
+ val items = listOf(appLocation, appMicrophone, appCamera)
+ val textBuilder = PrivacyChipBuilder(context, items)
+ val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+ assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
new file mode 100644
index 000000000000..dddc35072315
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class PrivacyItemControllerTest : SysuiTestCase() {
+
+ companion object {
+ val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+ val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+ const val SYSTEM_UID = 1000
+ const val TEST_PACKAGE_NAME = "test"
+ const val DEVICE_SERVICES_STRING = "Device services"
+ const val TAG = "PrivacyItemControllerTest"
+ fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
+ }
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var callback: PrivacyItemController.Callback
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var dumpManager: DumpManager
+ @Captor
+ private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
+ @Captor
+ private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+
+ private lateinit var privacyItemController: PrivacyItemController
+ private lateinit var executor: FakeExecutor
+ private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+ fun PrivacyItemController(context: Context): PrivacyItemController {
+ return PrivacyItemController(
+ context,
+ appOpsController,
+ executor,
+ executor,
+ broadcastDispatcher,
+ deviceConfigProxy,
+ userManager,
+ dumpManager
+ )
+ }
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ executor = FakeExecutor(FakeSystemClock())
+ deviceConfigProxy = DeviceConfigProxyFake()
+
+ appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
+
+ deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "true", false)
+
+ doReturn(listOf(object : UserInfo() {
+ init {
+ id = CURRENT_USER_ID
+ }
+ })).`when`(userManager).getProfiles(anyInt())
+
+ privacyItemController = PrivacyItemController(mContext)
+ }
+
+ @Test
+ fun testSetListeningTrueByAddingCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testSetListeningFalseByRemovingLastCallback() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).removeCallback(any(),
+ any())
+ privacyItemController.removeCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
+ any())
+ verify(callback).onPrivacyItemsChanged(emptyList())
+ }
+
+ @Test
+ fun testDistinctItems() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ }
+
+ @Test
+ fun testRegisterReceiver_allUsers() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
+ eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
+ verify(broadcastDispatcher, never())
+ .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
+ }
+
+ @Test
+ fun testReceiver_ACTION_USER_FOREGROUND() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_USER_SWITCHED))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+ privacyItemController.userSwitcherReceiver.onReceive(context,
+ Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE))
+ executor.runAllReady()
+ verify(userManager).getProfiles(anyInt())
+ }
+
+ @Test
+ fun testAddMultipleCallbacks() {
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ // Adding a callback should not unnecessarily call previous ones
+ verifyNoMoreInteractions(callback)
+ }
+
+ @Test
+ fun testMultipleCallbacksAreUpdated() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testRemoveCallback() {
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ val otherCallback = mock(PrivacyItemController.Callback::class.java)
+ privacyItemController.addCallback(callback)
+ privacyItemController.addCallback(otherCallback)
+ executor.runAllReady()
+ executor.runAllReady()
+ reset(callback)
+ reset(otherCallback)
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ privacyItemController.removeCallback(callback)
+ argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+ executor.runAllReady()
+ verify(callback, never()).onPrivacyItemsChanged(anyList())
+ verify(otherCallback).onPrivacyItemsChanged(anyList())
+ }
+
+ @Test
+ fun testListShouldNotHaveNull() {
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ executor.runAllReady()
+
+ verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+ assertEquals(1, argCaptor.value.size)
+ assertThat(argCaptor.value, not(hasItem(nullValue())))
+ }
+
+ @Test
+ fun testListShouldBeCopy() {
+ val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+ PrivacyApplication("", TEST_UID)))
+ privacyItemController.privacyList = list
+ val privacyList = privacyItemController.privacyList
+ assertEquals(list, privacyList)
+ assertTrue(list !== privacyList)
+ }
+
+ @Test
+ fun testNotListeningWhenIndicatorsDisabled() {
+ deviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+ "false",
+ false
+ )
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+ verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
+ any())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
new file mode 100644
index 000000000000..4aaafbdaec1d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ActionProxyReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private StatusBar mMockStatusBar;
+ @Mock
+ private ActivityManagerWrapper mMockActivityManagerWrapper;
+ @Mock
+ private Future mMockFuture;
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private Intent mIntent;
+
+ @Before
+ public void setup() throws InterruptedException, ExecutionException, TimeoutException {
+ MockitoAnnotations.initMocks(this);
+ mIntent = new Intent(mContext, ActionProxyReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+
+ when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
+ when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
+ }
+
+ @Test
+ public void testPendingIntentSentWithoutStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(false);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+ any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ // ensure that the pending intent call is passed through
+ doAnswer((Answer<Object>) invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+ verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+ any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
+ verify(mMockPendingIntent).send(
+ eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ }
+
+ @Test
+ public void testSmartActionsNotNotifiedByDefault() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions, never())
+ .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean());
+ }
+
+ @Test
+ public void testSmartActionsNotifiedIfEnabled() {
+ ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+ mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+ String testId = "testID";
+ mIntent.putExtra(EXTRA_ID, testId);
+
+ actionProxyReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_SHARE, false);
+ }
+
+ private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
+ if (withStatusBar) {
+ return new ActionProxyReceiver(
+ Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+ mMockScreenshotSmartActions);
+ } else {
+ return new ActionProxyReceiver(
+ Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
new file mode 100644
index 000000000000..b9249131c191
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeleteScreenshotReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private Executor mMockExecutor;
+
+ private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDeleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
+ }
+
+ @Test
+ public void testNoUriProvided() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor, never()).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testFileDeleted() {
+ DeleteScreenshotReceiver deleteScreenshotReceiver =
+ new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final Uri testUri = contentResolver.insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
+ assertNotNull(testUri);
+
+ try {
+ Cursor cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(1, cursor.getCount());
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
+ .putExtra(SCREENSHOT_URI_ID, testUri.toString());
+
+ deleteScreenshotReceiver.onReceive(mContext, intent);
+ int runCount = mFakeExecutor.runAllReady();
+
+ assertEquals(1, runCount);
+ cursor =
+ contentResolver.query(testUri, null, null, null, null);
+ assertEquals(0, cursor.getCount());
+ } finally {
+ contentResolver.delete(testUri, null, null);
+ }
+
+ // ensure smart actions not called by default
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+ any(Context.class), any(String.class), any(String.class), anyBoolean());
+ }
+
+ @Test
+ public void testNotifyScreenshotAction() {
+ Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+ String uriString = "testUri";
+ String testId = "testID";
+ intent.putExtra(SCREENSHOT_URI_ID, uriString);
+ intent.putExtra(EXTRA_ID, testId);
+ intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+
+ mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+ verify(mMockExecutor).execute(any(Runnable.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, ACTION_TYPE_DELETE, false);
+ }
+
+ private static ContentValues getFakeContentValues() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
+ values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
+ values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
+ return values;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index d3b33992d017..184329ec6e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -61,12 +61,14 @@ import java.util.concurrent.TimeUnit;
*/
public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+ private ScreenshotSmartActions mScreenshotSmartActions;
private Handler mHandler;
@Before
public void setup() {
mSmartActionsProvider = mock(
ScreenshotNotificationSmartActionsProvider.class);
+ mScreenshotSmartActions = new ScreenshotSmartActions();
mHandler = mock(Handler.class);
}
@@ -82,7 +84,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
.thenThrow(RuntimeException.class);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -100,7 +102,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
int timeoutMs = 1000;
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
RuntimeException.class);
- List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
+ List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
"", smartActionsFuture, timeoutMs, mSmartActionsProvider);
assertEquals(Collections.emptyList(), actions);
}
@@ -111,7 +113,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
throws Exception {
doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
anyLong());
- ScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+ mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
}
// Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
@@ -122,7 +124,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
@@ -136,7 +138,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
- ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
UserHandle.getUserHandleForUid(UserHandle.myUserId()));
verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
@@ -152,7 +154,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
mContext, null, mHandler);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
+ mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
actionsProvider,
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
assertNotNull(smartActionsFuture);
@@ -172,7 +174,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -198,7 +201,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
@@ -224,7 +228,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+ SaveImageInBackgroundTask task =
+ new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
Notification.Action deleteAction = task.createDeleteAction(mContext,
mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
new file mode 100644
index 000000000000..ce6f0736ec33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SmartActionsReceiverTest extends SysuiTestCase {
+
+ @Mock
+ private ScreenshotSmartActions mMockScreenshotSmartActions;
+ @Mock
+ private PendingIntent mMockPendingIntent;
+
+ private SmartActionsReceiver mSmartActionsReceiver;
+ private Intent mIntent;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
+ mIntent = new Intent(mContext, SmartActionsReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+ }
+
+ @Test
+ public void testSmartActionIntent() throws PendingIntent.CanceledException {
+ String testId = "testID";
+ String testActionType = "testActionType";
+ mIntent.putExtra(EXTRA_ID, testId);
+ mIntent.putExtra(EXTRA_ACTION_TYPE, testActionType);
+
+ mSmartActionsReceiver.onReceive(mContext, mIntent);
+
+ verify(mMockPendingIntent).send(
+ eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+ mContext, testId, testActionType, true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index aefea57155bc..8a49326add25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -61,6 +61,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index c5374b2eb049..601df2cb4fc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -184,7 +184,9 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
() -> mRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b286f9486e13..6d411333b220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -55,6 +55,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -92,6 +93,7 @@ import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.leak.LeakDetector;
@@ -190,7 +192,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
() -> mock(NotificationRowBinder.class),
() -> mRemoteInputManager,
mock(LeakDetector.class),
- mock(ForegroundServiceDismissalFeatureController.class)
+ mock(ForegroundServiceDismissalFeatureController.class),
+ mock(HeadsUpManager.class),
+ mock(StatusBarStateController.class)
);
mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index debc840394b5..fa253e62ef0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
+import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
import org.junit.Assert;
@@ -50,6 +56,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
+ @Mock private BatteryController mBatteryController;
@Before
public void setup() {
@@ -59,11 +66,12 @@ public class DozeParametersTest extends SysuiTestCase {
mAmbientDisplayConfiguration,
mAlwaysOnDisplayPolicy,
mPowerManager,
+ mBatteryController,
mTunerService
);
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
mDozeParameters.setControlScreenOffAnimation(true);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(false);
@@ -71,7 +79,7 @@ public class DozeParametersTest extends SysuiTestCase {
}
@Test
- public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+ public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
mDozeParameters.setControlScreenOffAnimation(false);
reset(mPowerManager);
mDozeParameters.setControlScreenOffAnimation(true);
@@ -79,11 +87,28 @@ public class DozeParametersTest extends SysuiTestCase {
}
@Test
- public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
+ public void testGetWallpaperAodDuration_when_shouldControlScreenOff() {
mDozeParameters.setControlScreenOffAnimation(true);
Assert.assertEquals(
"wallpaper hides faster when controlling screen off",
mDozeParameters.getWallpaperAodDuration(),
DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
}
+
+ @Test
+ public void testGetAlwaysOn() {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isTrue();
+ }
+
+ @Test
+ public void testGetAlwaysOn_whenBatterySaver() {
+ when(mBatteryController.isAodPowerSave()).thenReturn(true);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+ assertThat(mDozeParameters.getAlwaysOn()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 4d6922c02c41..0c2361a9f6b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify;
import android.app.AppOpsManager;
import android.content.Intent;
import android.location.LocationManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -60,8 +61,11 @@ public class LocationControllerImplTest extends SysuiTestCase {
mLocationController = spy(new LocationControllerImpl(mContext,
mAppOpsController,
mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class)));
+
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
new file mode 100644
index 000000000000..8cb5f3e65a5e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+ private final Map<SettingsKey, String> mValues = new HashMap<>();
+ private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
+ new HashMap<>();
+
+ public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+
+ public FakeSettings() {
+ }
+
+ public FakeSettings(String initialKey, String initialValue) {
+ putString(initialKey, initialValue);
+ }
+
+ public FakeSettings(Map<String, String> initialValues) {
+ for (Map.Entry<String, String> kv : initialValues.entrySet()) {
+ putString(kv.getKey(), kv.getValue());
+ }
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
+ public void registerContentObserverForUser(String name, ContentObserver settingsObserver,
+ int userHandle) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mContentObservers.putIfAbsent(key, new ArrayList<>());
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.add(settingsObserver);
+ }
+
+ @Override
+ public void unregisterContentObserver(ContentObserver settingsObserver) {
+ for (SettingsKey key : mContentObservers.keySet()) {
+ List<ContentObserver> observers = mContentObservers.get(key);
+ observers.remove(settingsObserver);
+ }
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Uri.withAppendedPath(CONTENT_URI, name);
+ }
+
+ @Override
+ public int getUserId() {
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ @Override
+ public String getStringForUser(String name, int userHandle) {
+ return mValues.get(new SettingsKey(userHandle, name));
+ }
+
+ @Override
+ public boolean putString(String name, String value, boolean overrideableByRestore) {
+ return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
+ }
+
+ @Override
+ public boolean putString(String name, String value) {
+ return putString(name, value, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, int userHandle) {
+ return putStringForUser(name, value, null, false, userHandle, false);
+ }
+
+ @Override
+ public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+ int userHandle, boolean overrideableByRestore) {
+ SettingsKey key = new SettingsKey(userHandle, name);
+ mValues.put(key, value);
+
+ Uri uri = getUriFor(name);
+ for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), userHandle);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ return putString(name, value);
+ }
+
+ private static class SettingsKey extends Pair<Integer, String> {
+ SettingsKey(Integer first, String second) {
+ super(first, second);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
new file mode 100644
index 000000000000..0d560f237a07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.systemui.util.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collection;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeSettingsTest extends SysuiTestCase {
+ @Mock
+ ContentObserver mContentObserver;
+
+ private FakeSettings mFakeSettings;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeSettings = new FakeSettings();
+ }
+
+ /**
+ * Test FakeExecutor that receives non-delayed items to execute.
+ */
+ @Test
+ public void testPutAndGet() throws Settings.SettingNotFoundException {
+ mFakeSettings.putInt("foobar", 1);
+ assertThat(mFakeSettings.getInt("foobar")).isEqualTo(1);
+ }
+
+ @Test
+ public void testInitialize() {
+ mFakeSettings = new FakeSettings("abra", "cadabra");
+ assertThat(mFakeSettings.getString("abra")).isEqualTo("cadabra");
+ }
+
+ @Test
+ public void testInitializeWithMap() {
+ mFakeSettings = new FakeSettings(Map.of("one fish", "two fish", "red fish", "blue fish"));
+ assertThat(mFakeSettings.getString("red fish")).isEqualTo("blue fish");
+ assertThat(mFakeSettings.getString("one fish")).isEqualTo("two fish");
+ }
+
+ @Test
+ public void testRegisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+ }
+
+ @Test
+ public void testUnregisterContentObserver() {
+ mFakeSettings.registerContentObserver("cat", mContentObserver);
+ mFakeSettings.unregisterContentObserver(mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver, never()).dispatchChange(
+ anyBoolean(), any(Collection.class), anyInt());
+ }
+}
diff --git a/services/Android.bp b/services/Android.bp
index 40b925de95d6..f0144ac1c695 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -154,10 +154,14 @@ droidstubs {
java_library {
name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
srcs: [":services-stubs.sources"],
installable: false,
static_libs: ["android_module_lib_stubs_current"],
sdk_version: "none",
system_modules: "none",
java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7f6dc14f3793..c87dcd7874f8 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,6 +75,16 @@ public class GestureLauncherService extends SystemService {
*/
@VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500;
+ /**
+ * Number of taps required to launch panic ui.
+ */
+ private static final int PANIC_POWER_TAP_COUNT_THRESHOLD = 5;
+
+ /**
+ * Number of taps required to launch camera shortcut.
+ */
+ private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -135,6 +145,7 @@ public class GestureLauncherService extends SystemService {
private long mLastPowerDown;
private int mPowerButtonConsecutiveTaps;
+ private int mPowerButtonSlowConsecutiveTaps;
public GestureLauncherService(Context context) {
this(context, new MetricsLogger());
@@ -350,9 +361,8 @@ public class GestureLauncherService extends SystemService {
* Whether to enable panic button gesture.
*/
public static boolean isPanicButtonGestureEnabled(Context context, int userId) {
- return isCameraLaunchEnabled(context.getResources())
- && (Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0);
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0;
}
/**
@@ -384,6 +394,13 @@ public class GestureLauncherService extends SystemService {
isCameraLiftTriggerEnabled(resources);
}
+ /**
+ * Attempts to intercept power key down event by detecting certain gesture patterns
+ *
+ * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
+ * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
+ * @return true if the key down event is intercepted
+ */
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
MutableBoolean outLaunched) {
if (event.isLongPress()) {
@@ -392,41 +409,60 @@ public class GestureLauncherService extends SystemService {
// taps or consecutive taps, so we want to ignore the long press event.
return false;
}
- boolean launched = false;
+ boolean launchCamera = false;
+ boolean launchPanic = false;
boolean intercept = false;
long powerTapInterval;
synchronized (this) {
powerTapInterval = event.getEventTime() - mLastPowerDown;
- if (mCameraDoubleTapPowerEnabled
- && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
- launched = true;
- intercept = interactive;
- mPowerButtonConsecutiveTaps++;
- } else if (powerTapInterval < POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
- mPowerButtonConsecutiveTaps++;
- } else {
+ mLastPowerDown = event.getEventTime();
+ if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+ // Tap too slow, reset consecutive tap counts.
+ mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps = 1;
+ } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+ // Tap too slow for shortcuts
mPowerButtonConsecutiveTaps = 1;
+ mPowerButtonSlowConsecutiveTaps++;
+ } else {
+ // Fast consecutive tap
+ mPowerButtonConsecutiveTaps++;
+ mPowerButtonSlowConsecutiveTaps++;
+ }
+ if (mPanicButtonGestureEnabled
+ && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+ launchPanic = true;
+ intercept = interactive;
+ } else if (mCameraDoubleTapPowerEnabled
+ && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+ && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+ launchCamera = true;
+ intercept = interactive;
}
- mLastPowerDown = event.getEventTime();
}
- if (DBG && mPowerButtonConsecutiveTaps > 1) {
- Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps) +
- " consecutive power button taps detected");
+ if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
+ Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
+ + " consecutive power button taps detected, "
+ + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
+ + " consecutive slow power button taps detected");
}
- if (launched) {
+ if (launchCamera) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+ powerTapInterval + "ms");
- launched = handleCameraGesture(false /* useWakelock */,
+ launchCamera = handleCameraGesture(false /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
- if (launched) {
+ if (launchCamera) {
mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) powerTapInterval);
}
+ } else if (launchPanic) {
+ Slog.i(TAG, "Panic gesture detected, launching panic.");
}
- mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonConsecutiveTaps);
+ mMetricsLogger.histogram("power_consecutive_short_tap_count",
+ mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launched;
- return intercept && launched;
+ outLaunched.value = launchCamera || launchPanic;
+ return intercept && (launchCamera || launchPanic);
}
/**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 816d663e09de..1520dd351c97 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3238,6 +3238,12 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void lockUserKey(int userId) {
+ // Do not lock user 0 data for headless system user
+ if (userId == UserHandle.USER_SYSTEM
+ && UserManager.isHeadlessSystemUserMode()) {
+ throw new IllegalArgumentException("Headless system user data cannot be locked..");
+ }
+
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
try {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index be080e5cce62..915189c085c2 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -81,6 +81,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import static android.app.UiModeManager.MODE_NIGHT_YES;
@@ -1446,6 +1447,8 @@ final class UiModeManagerService extends SystemService {
pw.println(" Print this help text.");
pw.println(" night [yes|no|auto|custom]");
pw.println(" Set or read night mode.");
+ pw.println(" car [yes|no]");
+ pw.println(" Set or read car mode.");
pw.println(" time [start|end] <ISO time>");
pw.println(" Set custom start/end schedule time"
+ " (night mode must be set to custom to apply).");
@@ -1461,6 +1464,8 @@ final class UiModeManagerService extends SystemService {
switch (cmd) {
case "night":
return handleNightMode();
+ case "car":
+ return handleCarMode();
case "time":
return handleCustomTime();
default:
@@ -1558,6 +1563,34 @@ final class UiModeManagerService extends SystemService {
return -1;
}
}
+
+ private int handleCarMode() throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ final String modeStr = getNextArg();
+ if (modeStr == null) {
+ printCurrentCarMode();
+ return 0;
+ }
+
+ if (modeStr.equals("yes")) {
+ mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
+ printCurrentCarMode();
+ return 0;
+ } else if (modeStr.equals("no")) {
+ mInterface.disableCarMode(0 /* flags */);
+ printCurrentCarMode();
+ return 0;
+ } else {
+ err.println("Error: mode must be 'yes', or 'no'");
+ return -1;
+ }
+ }
+
+ private void printCurrentCarMode() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int currMode = mInterface.getCurrentModeType();
+ pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
+ }
}
public final class LocalService extends UiModeManagerInternal {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ae89f37fa80b..bd51c7a1773d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -690,7 +690,7 @@ public final class ActiveServices {
}
if (allowBackgroundActivityStarts) {
- r.whitelistBgActivityStartsOnServiceStart();
+ r.allowBgActivityStartsOnServiceStart();
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
@@ -2045,7 +2045,7 @@ public final class ActiveServices {
s.whitelistManager = true;
}
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.setHasBindingWhitelistingBgActivityStarts(true);
+ s.setAllowedBgActivityStartsByBinding(true);
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
@@ -3457,9 +3457,9 @@ public final class ActiveServices {
updateWhitelistManagerLocked(s.app);
}
}
- // And do the same for bg activity starts whitelisting.
+ // And do the same for bg activity starts ability.
if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.updateHasBindingWhitelistingBgActivityStarts();
+ s.updateIsAllowedBgActivityStartsByBinding();
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 09ed16ef793b..775119c18037 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -253,7 +253,8 @@ final class ActivityManagerConstants extends ContentObserver {
// allowing the next pending start to run.
public long BG_START_TIMEOUT = DEFAULT_BG_START_TIMEOUT;
- // For how long after a whitelisted service's start its process can start a background activity
+ // For a service that has been allowed to start background activities, how long after it started
+ // its process can start a background activity.
public long SERVICE_BG_ACTIVITY_START_TIMEOUT = DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT;
// Initial backoff delay for retrying bound foreground services
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 57250d52b2ab..cfd2bf913b9c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -291,6 +291,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -310,6 +311,7 @@ import android.view.autofill.AutofillManagerInternal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ProcessMap;
@@ -657,6 +659,14 @@ public class ActivityManagerService extends IActivityManager.Stub
int mUidChangeDispatchCount;
/**
+ * Uids of apps with current active camera sessions. Access synchronized on
+ * the IntArray instance itself, and no other locks must be acquired while that
+ * one is held.
+ */
+ @GuardedBy("mActiveCameraUids")
+ final IntArray mActiveCameraUids = new IntArray(4);
+
+ /**
* Helper class which strips out priority and proto arguments then calls the dump function with
* the appropriate arguments. If priority arguments are omitted, function calls the legacy
* dump command.
@@ -2053,7 +2063,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (proc != null) {
long startTime = SystemClock.currentThreadTimeMillis();
- long pss = Debug.getPss(pid, tmp, null);
+ // skip background PSS calculation of apps that are capturing
+ // camera imagery
+ final boolean usingCamera = isCameraActiveForUid(proc.uid);
+ long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (ActivityManagerService.this) {
if (pss != 0 && proc.thread != null && proc.setProcState == procState
@@ -2066,6 +2079,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessList.abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
": " + (proc.thread == null ? "NO_THREAD " : "") +
+ (usingCamera ? "CAMERA " : "") +
(proc.pid != pid ? "PID_CHANGED " : "") +
" initState=" + procState + " curState=" +
proc.setProcState + " " +
@@ -2155,6 +2169,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
});
+
+ final int[] cameraOp = {AppOpsManager.OP_CAMERA};
+ mAppOpsService.startWatchingActive(cameraOp, new IAppOpsActiveCallback.Stub() {
+ @Override
+ public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+ cameraActiveChanged(uid, active);
+ }
+ });
}
public void setWindowManager(WindowManagerService wm) {
@@ -18266,6 +18288,27 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ final void cameraActiveChanged(@UserIdInt int uid, boolean active) {
+ synchronized (mActiveCameraUids) {
+ final int curIndex = mActiveCameraUids.indexOf(uid);
+ if (active) {
+ if (curIndex < 0) {
+ mActiveCameraUids.add(uid);
+ }
+ } else {
+ if (curIndex >= 0) {
+ mActiveCameraUids.remove(curIndex);
+ }
+ }
+ }
+ }
+
+ final boolean isCameraActiveForUid(@UserIdInt int uid) {
+ synchronized (mActiveCameraUids) {
+ return mActiveCameraUids.indexOf(uid) >= 0;
+ }
+ }
+
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
@@ -20518,4 +20561,17 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ @Override
+ public void resetAppErrors() {
+ enforceCallingPermission(Manifest.permission.RESET_APP_ERRORS, "resetAppErrors");
+ synchronized (this) {
+ mAppErrors.resetStateLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index a36a18b4cf5c..2e92ac0fb3d6 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -107,6 +107,16 @@ class AppErrors {
mPackageWatchdog = watchdog;
}
+ /** Resets the current state but leaves the constructor-provided fields unchanged. */
+ public void resetStateLocked() {
+ Slog.i(TAG, "Resetting AppErrors");
+ mAppsNotReportingCrashes.clear();
+ mProcessCrashTimes.clear();
+ mProcessCrashTimesPersistent.clear();
+ mProcessCrashShowDialogTimes.clear();
+ mBadProcesses.clear();
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
return;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index be17b1bc600c..494f06ebc324 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -62,7 +62,8 @@ public class BroadcastConstants {
public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
// Minimum that the deferral time can decay to until the backlog fully clears
public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
- // For how long after a whitelisted receiver's start its process can start a background activity
+ // For a receiver that has been allowed to start background activities, how long after it
+ // started its process can start a background activity.
public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
// Settings override tracking for this instance
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8ef67f97e8d4..40743b8be1ea 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -89,8 +89,8 @@ final class BroadcastRecord extends Binder {
int manifestSkipCount; // number of manifest receivers skipped.
BroadcastQueue queue; // the outbound queue handling this broadcast
- // if set to true, app's process will be temporarily whitelisted to start activities
- // from background for the duration of the broadcast dispatch
+ // if set to true, app's process will be temporarily allowed to start activities from background
+ // for the duration of the broadcast dispatch
final boolean allowBackgroundActivityStarts;
static final int IDLE = 0;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a526165..1038069f5d11 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2050,7 +2050,9 @@ public final class ProcessList {
final int pid = precedence.pid;
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
+ final int oldPolicy = StrictMode.getThreadPolicyMask();
try {
+ StrictMode.setThreadPolicyMask(0);
Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
synchronized (precedence) {
@@ -2069,9 +2071,11 @@ public final class ProcessList {
}
}
} catch (Exception e) {
- // It's still alive...
+ // It's still alive... maybe blocked at uninterruptible sleep ?
Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
- + app);
+ + app, e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldPolicy);
}
}
try {
@@ -2416,7 +2420,15 @@ public final class ProcessList {
ProcessList.killProcessGroup(app.uid, app.pid);
checkSlow(startTime, "startProcess: done killing old proc");
- Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+ // Throw a wtf if it's not killed, or killed but not because the system was in
+ // memory pressure + the app was in "cch-empty" and used large amount of memory
+ Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ } else {
+ Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ }
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
// routine of it yet, but we'd set it as the precedence of the new process.
precedence = app;
@@ -2819,7 +2831,15 @@ public final class ProcessList {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ if (old.killed) {
+ // The old process has been killed, we probably haven't had
+ // a chance to clean up the old record, just log a warning
+ Slog.w(TAG, "Existing proc " + old + " was killed "
+ + (SystemClock.uptimeMillis() - old.mKillTime)
+ + "ms ago when adding " + proc);
+ } else {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c5152c081e70..6e1bd8faeaf9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -274,7 +274,7 @@ class ProcessRecord implements WindowProcessListener {
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
- // A set of tokens that currently contribute to this process being temporarily whitelisted
+ // A set of tokens that currently contribute to this process being temporarily allowed
// to start activities even if it's not in the foreground
final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
// a set of UIDs of all bound clients
@@ -352,6 +352,8 @@ class ProcessRecord implements WindowProcessListener {
boolean mReachable; // Whether or not this process is reachable from given process
+ long mKillTime; // The timestamp in uptime when this process was killed.
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
this.startUid = startUid;
@@ -626,7 +628,7 @@ class ProcessRecord implements WindowProcessListener {
}
}
if (mAllowBackgroundActivityStartsTokens.size() > 0) {
- pw.print(prefix); pw.println("Background activity start whitelist tokens:");
+ pw.print(prefix); pw.println("Background activity start tokens:");
for (int i = 0; i < mAllowBackgroundActivityStartsTokens.size(); i++) {
pw.print(prefix); pw.print(" - ");
pw.println(mAllowBackgroundActivityStartsTokens.valueAt(i));
@@ -925,6 +927,7 @@ class ProcessRecord implements WindowProcessListener {
if (!mPersistent) {
killed = true;
killedByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 828ac71eccfe..db05d65b92fe 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -131,13 +131,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects
// any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
- private boolean mHasBindingWhitelistingBgActivityStarts;
- // is this service currently whitelisted to start activities from background by providing
+ private boolean mIsAllowedBgActivityStartsByBinding;
+ // is this service currently allowed to start activities from background by providing
// allowBackgroundActivityStarts=true to startServiceLocked()?
- private boolean mHasStartedWhitelistingBgActivityStarts;
- // used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout
- private Runnable mStartedWhitelistingBgActivityStartsCleanUp;
- private ProcessRecord mAppForStartedWhitelistingBgActivityStarts;
+ private boolean mIsAllowedBgActivityStartsByStart;
+ // used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
+ private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
+ private ProcessRecord mAppForAllowingBgActivityStartsByStart;
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -396,13 +396,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (whitelistManager) {
pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
}
- if (mHasBindingWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts=");
- pw.println(mHasBindingWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByBinding) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding=");
+ pw.println(mIsAllowedBgActivityStartsByBinding);
}
- if (mHasStartedWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
- pw.println(mHasStartedWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByStart) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
+ pw.println(mIsAllowedBgActivityStartsByStart);
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
@@ -560,31 +560,31 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
public void setProcess(ProcessRecord _proc) {
if (_proc != null) {
- // We're starting a new process for this service, but a previous one is whitelisted.
- // Remove that whitelisting now (unless the new process is the same as the previous one,
- // which is a common case).
- if (mAppForStartedWhitelistingBgActivityStarts != null) {
- if (mAppForStartedWhitelistingBgActivityStarts != _proc) {
- mAppForStartedWhitelistingBgActivityStarts
+ // We're starting a new process for this service, but a previous one is allowed to start
+ // background activities. Remove that ability now (unless the new process is the same as
+ // the previous one, which is a common case).
+ if (mAppForAllowingBgActivityStartsByStart != null) {
+ if (mAppForAllowingBgActivityStartsByStart != _proc) {
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(this);
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
}
}
// Make sure the cleanup callback knows about the new process.
- mAppForStartedWhitelistingBgActivityStarts = mHasStartedWhitelistingBgActivityStarts
+ mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
? _proc : null;
- if (mHasStartedWhitelistingBgActivityStarts
- || mHasBindingWhitelistingBgActivityStarts) {
+ if (mIsAllowedBgActivityStartsByStart
+ || mIsAllowedBgActivityStartsByBinding) {
_proc.addAllowBackgroundActivityStartsToken(this);
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
}
if (app != null && app != _proc) {
- // If the old app is whitelisted because of a service start, leave it whitelisted until
- // the cleanup callback runs. Otherwise we can remove it from the whitelist immediately
- // (it can't be bound now).
- if (!mHasStartedWhitelistingBgActivityStarts) {
+ // If the old app is allowed to start bg activities because of a service start, leave it
+ // that way until the cleanup callback runs. Otherwise we can remove its bg activity
+ // start ability immediately (it can't be bound now).
+ if (!mIsAllowedBgActivityStartsByStart) {
app.removeAllowBackgroundActivityStartsToken(this);
}
app.updateBoundClientUids();
@@ -648,89 +648,88 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();
}
- void updateHasBindingWhitelistingBgActivityStarts() {
- boolean hasWhitelistingBinding = false;
+ void updateIsAllowedBgActivityStartsByBinding() {
+ boolean isAllowedByBinding = false;
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i = 0; i < cr.size(); i++) {
if ((cr.get(i).flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- hasWhitelistingBinding = true;
+ isAllowedByBinding = true;
break;
}
}
- if (hasWhitelistingBinding) {
+ if (isAllowedByBinding) {
break;
}
}
- setHasBindingWhitelistingBgActivityStarts(hasWhitelistingBinding);
+ setAllowedBgActivityStartsByBinding(isAllowedByBinding);
}
- void setHasBindingWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasBindingWhitelistingBgActivityStarts != newValue) {
- mHasBindingWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ void setAllowedBgActivityStartsByBinding(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByBinding != newValue) {
+ mIsAllowedBgActivityStartsByBinding = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Called when the service is started with allowBackgroundActivityStarts set. We whitelist
- * it for background activity starts, setting up a callback to remove the whitelisting after a
- * timeout. Note that the whitelisting persists for the process even if the service is
- * subsequently stopped.
+ * Called when the service is started with allowBackgroundActivityStarts set. We allow
+ * it for background activity starts, setting up a callback to remove this ability after a
+ * timeout. Note that the ability for starting background activities persists for the process
+ * even if the service is subsequently stopped.
*/
- void whitelistBgActivityStartsOnServiceStart() {
- setHasStartedWhitelistingBgActivityStarts(true);
+ void allowBgActivityStartsOnServiceStart() {
+ setAllowedBgActivityStartsByStart(true);
if (app != null) {
- mAppForStartedWhitelistingBgActivityStarts = app;
+ mAppForAllowingBgActivityStartsByStart = app;
}
// This callback is stateless, so we create it once when we first need it.
- if (mStartedWhitelistingBgActivityStartsCleanUp == null) {
- mStartedWhitelistingBgActivityStartsCleanUp = () -> {
+ if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
+ mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- if (app == mAppForStartedWhitelistingBgActivityStarts) {
- // The process we whitelisted is still running the service. We remove
- // the started whitelisting, but it may still be whitelisted via bound
- // connections.
- setHasStartedWhitelistingBgActivityStarts(false);
- } else if (mAppForStartedWhitelistingBgActivityStarts != null) {
- // The process we whitelisted is not running the service. It therefore
- // can't be bound so we can unconditionally remove the whitelist.
- mAppForStartedWhitelistingBgActivityStarts
+ if (app == mAppForAllowingBgActivityStartsByStart) {
+ // The process we allowed is still running the service. We remove
+ // the ability by start, but it may still be allowed via bound connections.
+ setAllowedBgActivityStartsByStart(false);
+ } else if (mAppForAllowingBgActivityStartsByStart != null) {
+ // The process we allowed is not running the service. It therefore can't be
+ // bound so we can unconditionally remove the ability.
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
}
- mAppForStartedWhitelistingBgActivityStarts = null;
+ mAppForAllowingBgActivityStartsByStart = null;
}
};
}
// if there's a request pending from the past, drop it before scheduling a new one
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
- ams.mHandler.postDelayed(mStartedWhitelistingBgActivityStartsCleanUp,
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
+ ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
- private void setHasStartedWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasStartedWhitelistingBgActivityStarts != newValue) {
- mHasStartedWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ private void setAllowedBgActivityStartsByStart(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByStart != newValue) {
+ mIsAllowedBgActivityStartsByStart = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Whether the process this service runs in should be temporarily whitelisted to start
+ * Whether the process this service runs in should be temporarily allowed to start
* activities from background depends on the current state of both
- * {@code hasStartedWhitelistingBgActivityStarts} and
- * {@code hasBindingWhitelistingBgActivityStarts}. If either is true, this ServiceRecord
+ * {@code mIsAllowedBgActivityStartsByStart} and
+ * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
* @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens
*/
- private void updateParentProcessBgActivityStartsWhitelistingToken() {
+ private void updateParentProcessBgActivityStartsToken() {
if (app == null) {
return;
}
- if (mHasStartedWhitelistingBgActivityStarts || mHasBindingWhitelistingBgActivityStarts) {
+ if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
app.addAllowBackgroundActivityStartsToken(this);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 433317b038b3..e6480fc6cde8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2059,6 +2059,8 @@ public class AppOpsService extends IAppOpsService.Stub {
public void getHistoricalOps(int uid, String packageName, String attributionTag,
List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
int flags, RemoteCallback callback) {
+ PackageManager pm = mContext.getPackageManager();
+
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2066,8 +2068,16 @@ public class AppOpsService extends IAppOpsService.Stub {
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUid(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
- if (!isCallerSystem && !isCallerInstrumented) {
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
mHandler.post(() -> callback.sendResult(new Bundle()));
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3ff6ec1afa41..86e6a3220507 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -244,7 +244,7 @@ abstract class HdmiCecLocalDevice {
if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
return false;
}
- // Cache incoming message. Note that it caches only white-listed one.
+ // Cache incoming message if it is included in the list of cacheable opcodes.
mCecMessageCache.cacheMessage(message);
return onMessage(message);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index aed94fc85431..64d70d6601f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -225,7 +225,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) {
mWakeLock = new SystemWakeLock();
} else {
- // Create a dummy lock object that doesn't do anything about wake lock,
+ // Create a stub lock object that doesn't do anything about wake lock,
// hence allows the device to go to sleep even if it's the active source.
mWakeLock = new ActiveWakeLock() {
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 2c0ddaf35182..804cc92cca08 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1667,6 +1667,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (avr == null) {
return;
}
+ setArcStatus(false);
// Seq #44.
removeAction(RequestArcInitiationAction.class);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0154fe07a418..254285dfbd41 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2942,7 +2942,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
vis = 0;
}
if (!mCurPerceptible) {
- vis = 0;
+ vis &= ~InputMethodService.IME_VISIBLE;
}
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c8239762d..d933c109b27d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,9 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -120,10 +123,16 @@ import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.SystemAppForegroundHelper;
import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
import com.android.server.location.util.SystemSettingsHelper;
import com.android.server.location.util.SystemUserInfoHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -173,7 +182,7 @@ public class LocationManagerService extends ILocationManager.Stub {
publishBinderService(Context.LOCATION_SERVICE, mService);
// client caching behavior is only enabled after seeing the first invalidate
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
@@ -486,7 +495,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private void onLocationModeChanged(int userId) {
boolean enabled = mSettingsHelper.isLocationEnabled(userId);
- invalidateLocalLocationEnabledCaches();
+ LocationManager.invalidateLocalLocationEnabledCaches();
if (D) {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
@@ -1232,19 +1241,20 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!currentlyMonitoring) {
if (allowMonitoring) {
if (!highPower) {
- return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+ return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ mCallerIdentity);
}
}
} else {
- if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+ if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
- mCallerIdentity.getPid()))) {
+ mCallerIdentity.getPid())), mCallerIdentity)) {
if (!highPower) {
- mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
} else {
- mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
}
return false;
}
@@ -1589,8 +1599,9 @@ public class LocationManagerService extends ILocationManager.Stub {
continue;
}
- if (!mAppOpsHelper.checkLocationAccess(identity,
- record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+ record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+ identity)) {
continue;
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2118,7 +2129,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
return null;
}
@@ -2179,7 +2191,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
// appops check should always be right before delivery
- if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+ if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ identity)) {
transport.deliverResult(lastLocation);
} else {
transport.deliverResult(null);
@@ -2329,7 +2342,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ public void sendExtraCommand(String provider, String command, Bundle extras) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
mContext.enforceCallingOrSelfPermission(
permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2350,8 +2363,6 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
-
- return true;
}
@Override
@@ -2553,7 +2564,8 @@ public class LocationManagerService extends ILocationManager.Stub {
r.mLastFixBroadcast = location;
// appops check should always be right before delivery
- if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+ if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+ receiver.mCallerIdentity)) {
continue;
}
@@ -2644,7 +2656,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2664,7 +2676,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2687,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2708,7 +2720,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
attributionTag);
- if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+ if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
return;
}
@@ -2926,24 +2938,36 @@ public class LocationManagerService extends ILocationManager.Stub {
private final UserInfoHelper mUserInfoHelper;
private final SystemAppOpsHelper mAppOpsHelper;
+ private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
private final SystemSettingsHelper mSettingsHelper;
private final SystemAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mUserInfoHelper = userInfoHelper;
mAppOpsHelper = new SystemAppOpsHelper(context);
+ mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+ mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+ mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
void onSystemReady() {
mAppOpsHelper.onSystemReady();
+ mLocationPermissionsHelper.onSystemReady();
mSettingsHelper.onSystemReady();
mAppForegroundHelper.onSystemReady();
+ mLocationPowerSaveModeHelper.onSystemReady();
+ mScreenInteractiveHelper.onSystemReady();
}
@Override
@@ -2957,6 +2981,11 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -2972,8 +3001,23 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public ScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a456ac..f6896b86f9b9 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@ public class PassiveProvider extends AbstractLocationProvider {
Criteria.POWER_LOW,
Criteria.ACCURACY_COARSE);
- private volatile boolean mReportLocation;
-
public PassiveProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
- mReportLocation = false;
-
setProperties(PROPERTIES);
setAllowed(true);
}
@@ -66,15 +62,11 @@ public class PassiveProvider extends AbstractLocationProvider {
* Pass a location into the passive provider.
*/
public void updateLocation(Location location) {
- if (mReportLocation) {
- reportLocation(location);
- }
+ reportLocation(location);
}
@Override
- public void onSetRequest(ProviderRequest request) {
- mReportLocation = request.reportLocation;
- }
+ public void onSetRequest(ProviderRequest request) {}
@Override
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index c855a12606b2..2d9734ef0553 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
package com.android.server.location.geofence;
-import static android.Manifest.permission;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@ import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@ public class GeofenceManager extends
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
private @Nullable Location mCachedLocation;
private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@ public class GeofenceManager extends
mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TAG + ":" + identity.getPackageName());
+ TAG + ":" + identity.getPackageName());
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(identity.addToWorkSource(null));
}
@@ -114,7 +113,8 @@ public class GeofenceManager extends
@Override
protected void onPendingIntentListenerRegister() {
mGeofenceState = STATE_UNKNOWN;
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
}
@Override
@@ -127,18 +127,32 @@ public class GeofenceManager extends
}
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -186,10 +200,10 @@ public class GeofenceManager extends
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
try {
- pendingIntent.send(mContext, 0, intent,
- (pI, i, rC, rD, rE) -> mWakeLock.release(),
- null, permission.ACCESS_FINE_LOCATION,
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+ // send() only enforces permissions for broadcast intents, but since clients can
+ // select any kind of pending intent we do not rely on send() to enforce permissions
+ pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+ null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
} catch (PendingIntent.CanceledException e) {
mWakeLock.release();
removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -202,7 +216,7 @@ public class GeofenceManager extends
builder.append(getIdentity());
ArraySet<String> flags = new ArraySet<>(1);
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -224,10 +238,22 @@ public class GeofenceManager extends
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GeofenceManager.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GeofenceManager.this.onLocationPermissionsChanged(uid);
+ }
+ };
protected final UserInfoHelper mUserInfoHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final SettingsHelper mSettingsHelper;
protected final LocationUsageLogger mLocationUsageLogger;
@@ -241,7 +267,7 @@ public class GeofenceManager extends
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
}
@@ -281,7 +307,7 @@ public class GeofenceManager extends
@Override
protected boolean isActive(GeofenceRegistration registration) {
CallerIdentity identity = registration.getIdentity();
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
&& !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -294,7 +320,7 @@ public class GeofenceManager extends
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
}
@Override
@@ -303,7 +329,7 @@ public class GeofenceManager extends
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
}
@Override
@@ -434,7 +460,11 @@ public class GeofenceManager extends
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 53e660ad6475..0b7968be484b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@ import com.android.server.LocalServices;
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.UserInfoHelper;
import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
// we store these values because we don't trust the listeners not to give us dupes, not to
// spam us, and because checking the values may be more expensive
private boolean mForeground;
- private boolean mAppOpsAllowed;
+ private boolean mPermitted;
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
@@ -84,24 +84,39 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
return mForeground;
}
- boolean isAppOpsAllowed() {
- return mAppOpsAllowed;
+ boolean isPermitted() {
+ return mPermitted;
}
@Override
protected void onBinderListenerRegister() {
- mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+ mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
}
- boolean onAppOpsChanged(String packageName) {
+ boolean onLocationPermissionsChanged(String packageName) {
if (getIdentity().getPackageName().equals(packageName)) {
- boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
- PERMISSION_FINE);
- if (appOpsAllowed != mAppOpsAllowed) {
- mAppOpsAllowed = appOpsAllowed;
- return true;
- }
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ boolean onLocationPermissionsChanged(int uid) {
+ if (getIdentity().getUid() == uid) {
+ return onLocationPermissionsChanged();
+ }
+
+ return false;
+ }
+
+ private boolean onLocationPermissionsChanged() {
+ boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+ getIdentity());
+ if (permitted != mPermitted) {
+ mPermitted = permitted;
+ return true;
}
return false;
@@ -125,7 +140,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
if (!mForeground) {
flags.add("bg");
}
- if (!mAppOpsAllowed) {
+ if (!mPermitted) {
flags.add("na");
}
if (!flags.isEmpty()) {
@@ -141,7 +156,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
protected final UserInfoHelper mUserInfoHelper;
protected final SettingsHelper mSettingsHelper;
- protected final AppOpsHelper mAppOpsHelper;
+ protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
@@ -154,14 +169,26 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
this::onLocationPackageBlacklistChanged;
- private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+ private final LocationPermissionsHelper.LocationPermissionsListener
+ mLocationPermissionsListener =
+ new LocationPermissionsHelper.LocationPermissionsListener() {
+ @Override
+ public void onLocationPermissionsChanged(String packageName) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ public void onLocationPermissionsChanged(int uid) {
+ GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+ }
+ };
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
- mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
@@ -208,7 +235,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
CallerIdentity identity = registration.getIdentity();
// TODO: this should be checking if the gps provider is enabled, not if location is enabled,
// but this is the same for now.
- return registration.isAppOpsAllowed()
+ return registration.isPermitted()
&& (registration.isForeground() || isBackgroundRestrictionExempt(identity))
&& mUserInfoHelper.isCurrentUserId(identity.getUserId())
&& mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -241,7 +268,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.addListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
}
@@ -257,7 +284,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mBackgroundThrottlePackageWhitelistChangedListener);
mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
mLocationPackageBlacklistChangedListener);
- mAppOpsHelper.removeListener(mAppOpsChangedListener);
+ mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
}
@@ -279,8 +306,12 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onAppOpsChanged(String packageName) {
- updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+ private void onLocationPermissionsChanged(String packageName) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+ }
+
+ private void onLocationPermissionsChanged(int uid) {
+ updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
private void onAppForegroundChanged(int uid, boolean foreground) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 8aaf4bf6d8b0..8004ec70aaf3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2008,9 +2008,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private final class FusedLocationListener extends LocationChangeListener {
@Override
public void onLocationChanged(Location location) {
- if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
- injectBestLocation(location);
- }
+ injectBestLocation(location);
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725ca152d..8e81f29550d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@ package com.android.server.location.gnss;
import static android.location.LocationManager.GPS_PROVIDER;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import android.Manifest;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.location.GnssAntennaInfo;
import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
import java.io.FileDescriptor;
import java.util.List;
@@ -68,9 +65,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
}
private final Context mContext;
- private final SettingsHelper mSettingsHelper;
private final AppOpsHelper mAppOpsHelper;
- private final AppForegroundHelper mAppForegroundHelper;
private final LocationManagerInternal mLocationManagerInternal;
private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@ public class GnssManagerService implements GnssNative.Callbacks {
GnssNative.initialize();
mContext = context.createAttributionContext(ATTRIBUTION_ID);
- mSettingsHelper = injector.getSettingsHelper();
mAppOpsHelper = injector.getAppOpsHelper();
- mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@ public class GnssManagerService implements GnssNative.Callbacks {
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
String attributionTag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
- if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+ if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
return false;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a177f861..0815d46a735d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@ public class GnssMeasurementsProvider extends
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
SettingsHelper.GlobalSettingChangedListener {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
private final GnssMeasurementProviderNative mNative;
@@ -57,6 +59,7 @@ public class GnssMeasurementsProvider extends
@VisibleForTesting
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
mNative = aNative;
}
@@ -163,7 +166,8 @@ public class GnssMeasurementsProvider extends
*/
public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssMeasurementsReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe41c975..7dcffc664f52 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssNavigationMessage;
import android.location.IGnssNavigationMessageListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
/**
@@ -39,6 +40,7 @@ import com.android.server.location.util.Injector;
public class GnssNavigationMessageProvider extends
GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final GnssNavigationMessageProviderNative mNative;
public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@ public class GnssNavigationMessageProvider extends
public GnssNavigationMessageProvider(Injector injector,
GnssNavigationMessageProviderNative aNative) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mNative = aNative;
}
@@ -90,7 +93,8 @@ public class GnssNavigationMessageProvider extends
*/
public void onNavigationMessageAvailable(GnssNavigationMessage event) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onGnssNavigationMessageReceived(event);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b05866877..19f79273c992 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
package com.android.server.location.gnss;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.app.AppOpsManager;
import android.location.GnssStatus;
import android.location.IGnssStatusListener;
import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
@@ -35,10 +36,12 @@ import com.android.server.location.util.LocationUsageLogger;
*/
public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
+ private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
public GnssStatusProvider(Injector injector) {
super(injector);
+ mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
}
@@ -113,7 +116,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onSvStatusChanged(GnssStatus gnssStatus) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onSvStatusChanged(gnssStatus);
} else {
return null;
@@ -126,7 +130,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
*/
public void onNmeaReceived(long timestamp, String nmea) {
deliverToListeners(registration -> {
- if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+ if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+ registration.getIdentity())) {
return listener -> listener.onNmeaReceived(timestamp, nmea);
} else {
return null;
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f5889ceeed6a..528cf8acd5b3 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.Binder;
import android.os.Build;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -40,8 +41,8 @@ import java.util.function.Predicate;
* A base class to multiplex client listener registrations within system server. Registrations are
* divided into two categories, active registrations and inactive registrations, as defined by
* {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
- * {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
*
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
@@ -217,7 +218,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
mRegistrations.put(key, registration);
}
-
if (wasEmpty) {
onRegister();
}
@@ -268,7 +268,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TKey key = mRegistrations.keyAt(i);
if (predicate.test(key)) {
removeRegistration(key, mRegistrations.valueAt(i));
@@ -287,7 +288,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
* completely at some later time.
*/
protected final void removeRegistration(@NonNull Object key,
- @NonNull ListenerRegistration registration) {
+ @NonNull ListenerRegistration<?, ?> registration) {
synchronized (mRegistrations) {
int index = mRegistrations.indexOfKey(key);
if (index < 0) {
@@ -353,7 +354,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
actives.add(registration);
@@ -395,7 +397,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
protected final void updateService(Predicate<TRegistration> predicate) {
synchronized (mRegistrations) {
boolean updateService = false;
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration) && registration.isActive()) {
updateService = true;
@@ -434,7 +437,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (predicate.test(registration)) {
onRegistrationActiveChanged(registration);
@@ -446,33 +450,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
}
- /**
- * Evaluates the predicate on the registration with the given key. The predicate should return
- * true if the active state of the registration may have changed as a result. Any
- * {@link #updateService()} invocations made while this method is executing will be deferred
- * until after the method is complete so as to avoid redundant work.
- */
- protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
- synchronized (mRegistrations) {
- // since updating a registration can invoke a variety of callbacks, we need to ensure
- // those callbacks themselves do not re-enter, as this could lead to out-of-order
- // callbacks. note that try-with-resources ordering is meaningful here as well. we want
- // to close the reentrancy guard first, as this may generate additional service updates,
- // then close the update service buffer.
- long identity = Binder.clearCallingIdentity();
- try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
- ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
-
- TRegistration registration = mRegistrations.get(key);
- if (registration != null && predicate.test(registration)) {
- onRegistrationActiveChanged(registration);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
@GuardedBy("mRegistrations")
private void onRegistrationActiveChanged(TRegistration registration) {
if (Build.IS_DEBUGGABLE) {
@@ -511,7 +488,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
ListenerOperation<TListener> operation = function.apply(registration);
@@ -537,7 +515,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
synchronized (mRegistrations) {
long identity = Binder.clearCallingIdentity();
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
execute(registration, operation);
@@ -571,7 +550,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
ipw.println("listeners:");
ipw.increaseIndent();
- for (int i = 0; i < mRegistrations.size(); i++) {
+ final int size = mRegistrations.size();
+ for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
ipw.print(registration);
if (!registration.isActive()) {
@@ -612,23 +592,33 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
*/
private final class ReentrancyGuard implements AutoCloseable {
+ @GuardedBy("mRegistrations")
private int mGuardCount;
- private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+ @GuardedBy("mRegistrations")
+ private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
mScheduledRemovals = null;
}
+ @GuardedBy("mRegistrations")
boolean isReentrant() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
return mGuardCount != 0;
}
- void markForRemoval(Object key, ListenerRegistration registration) {
+ @GuardedBy("mRegistrations")
+ void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mRegistrations));
+ }
Preconditions.checkState(isReentrant());
if (mScheduledRemovals == null) {
- mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+ mScheduledRemovals = new ArraySet<>(mRegistrations.size());
}
mScheduledRemovals.add(new Pair<>(key, registration));
}
@@ -640,7 +630,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
@Override
public void close() {
- ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+ ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -650,8 +640,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
if (scheduledRemovals != null) {
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
- for (int i = 0; i < scheduledRemovals.size(); i++) {
- Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+ final int size = scheduledRemovals.size();
+ for (int i = 0; i < size; i++) {
+ Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+ i);
removeRegistration(pair.first, pair.second);
}
}
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index e529a7d81b70..6a815ead9f9f 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -61,7 +61,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
* Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
* after {@link #onUnregister()}. It is safe to invoke this from within either function.
*/
- public void remove() {
+ public final void remove() {
Object key = mKey;
if (key != null) {
getOwner().removeRegistration(key, this);
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27da78c..1578289d53b4 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
import android.location.util.identity.CallerIdentity;
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -70,84 +63,27 @@ public abstract class AppOpsHelper {
}
/**
- * Checks if the given identity may have locations delivered without noting that a location is
- * being delivered. This is a looser guarantee than
- * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
- * arguments and so should not be used with unvalidated arguments or before actually delivering
- * locations.
- *
- * @see AppOpsManager#checkOpNoThrow(int, int, String)
- */
- public final boolean checkLocationAccess(CallerIdentity callerIdentity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
- }
-
- /**
- * Notes location access to the given identity, ie, location delivery. This method should be
- * called right before a location is delivered, and if it returns false, the location should not
- * be delivered.
- */
- public final boolean noteLocationAccess(CallerIdentity identity,
- @PermissionLevel int permissionLevel) {
- if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
- return false;
- }
-
- return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
- }
-
- /**
- * Notifies app ops that the given identity is using location at normal/low power levels. If
- * this function returns false, do not later call
- * {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Starts the given appop.
*/
- public final boolean startLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_LOCATION, identity);
- }
+ public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at normal/low power
- * levels.
+ * Finishes the given appop.
*/
- public final void stopLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_LOCATION, identity);
- }
+ public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is using location at high levels. If this function
- * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+ * Checks the given appop.
*/
- public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
- return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
/**
- * Notifies app ops that the given identity is no longer using location at high power levels.
+ * Notes the given appop (and may throw a security exception).
*/
- public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
- finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- }
+ public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
/**
- * Notes access to any mock location APIs. If this call returns false, access to the APIs should
- * silently fail.
+ * Notes the given appop.
*/
- public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
- return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
- }
-
- protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
- protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+ public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
}
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5dc26cd..379b303bbfc3 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.util;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@ public interface Injector {
/** Returns an AppOpsHelper. */
AppOpsHelper getAppOpsHelper();
+ /** Returns a LocationPermissionsHelper. */
+ LocationPermissionsHelper getLocationPermissionsHelper();
+
/** Returns a SettingsHelper. */
SettingsHelper getSettingsHelper();
/** Returns an AppForegroundHelper. */
AppForegroundHelper getAppForegroundHelper();
- /** Returns a LocationUsageLogger. */
- LocationUsageLogger getLocationUsageLogger();
+ /** Returns a LocationPowerSaveModeHelper. */
+ LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+ /** Returns a ScreenInteractiveHelper. */
+ ScreenInteractiveHelper getScreenInteractiveHelper();
/** Returns a LocationAttributionHelper. */
LocationAttributionHelper getLocationAttributionHelper();
+
+ /** Returns a LocationUsageLogger. */
+ LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a LocationRequestStatistics. */
+ LocationRequestStatistics getLocationRequestStatistics();
}
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe09412c166..bc3ac0ff2e48 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -83,7 +90,7 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+ if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
mAttributions.remove(identity);
}
}
@@ -99,7 +106,7 @@ public class LocationAttributionHelper {
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
mAttributions.remove(identity);
- mAppOpsHelper.stopLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
}
}
@@ -113,14 +120,18 @@ public class LocationAttributionHelper {
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
if (keySet.add(new ProviderListener(provider, key)) && empty) {
- if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
+ }
+ } else {
mHighPowerAttributions.remove(identity);
}
}
}
/**
- * Report high power location usage has stopped for the given caller on the given provider,
+ * Report high power location usage has stopped for the given caller on the given provider,
* with a unique key.
*/
public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@ public class LocationAttributionHelper {
Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
if (keySet != null && keySet.remove(new ProviderListener(provider, key))
&& keySet.isEmpty()) {
+ if (D) {
+ Log.v(TAG, "stopping high power location attribution for " + identity);
+ }
mHighPowerAttributions.remove(identity);
- mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
}
}
}
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 000000000000..daf56797c0c9
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+ /**
+ * Listener for current user changes.
+ */
+ public interface LocationPermissionsListener {
+
+ /**
+ * Called when something has changed about location permissions for the given package.
+ */
+ void onLocationPermissionsChanged(String packageName);
+
+ /**
+ * Called when something has changed about location permissions for the given uid.
+ */
+ void onLocationPermissionsChanged(int uid);
+ }
+
+ private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+ private final AppOpsHelper mAppOps;
+
+ public LocationPermissionsHelper(AppOpsHelper appOps) {
+ mListeners = new CopyOnWriteArrayList<>();
+ mAppOps = appOps;
+
+ mAppOps.addListener(this::onAppOpsChanged);
+ }
+
+ protected final void notifyLocationPermissionsChanged(String packageName) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(packageName);
+ }
+ }
+
+ protected final void notifyLocationPermissionsChanged(int uid) {
+ for (LocationPermissionsListener listener : mListeners) {
+ listener.onLocationPermissionsChanged(uid);
+ }
+ }
+
+ private void onAppOpsChanged(String packageName) {
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ /**
+ * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+ */
+ public final void addListener(LocationPermissionsListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for location permissions events.
+ */
+ public final void removeListener(LocationPermissionsListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Returns true if the given identity may access location at the given permissions level, taking
+ * into account both permissions and appops.
+ */
+ public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+ CallerIdentity identity) {
+ if (permissionLevel == PERMISSION_NONE) {
+ return false;
+ }
+
+ if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+ return false;
+ }
+
+ return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ }
+
+ protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..a9a8c50f11dc
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+ /**
+ * Listener for location power save mode changes.
+ */
+ public interface LocationPowerSaveModeChangedListener {
+ /**
+ * Called when the location power save mode changes.
+ */
+ void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+ }
+
+ private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+ public LocationPowerSaveModeHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to location power save mode.
+ */
+ public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyLocationPowerSaveModeChanged(
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ for (LocationPowerSaveModeChangedListener listener : mListeners) {
+ listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+ }
+
+ /**
+ * Returns the current location power save mode.
+ */
+ @LocationPowerSaveMode
+ public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 000000000000..d47bce31ed23
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+ /**
+ * Listener for screen interactive changes.
+ */
+ public interface ScreenInteractiveChangedListener {
+ /**
+ * Called when the screen interative state changes.
+ */
+ void onScreenInteractiveChanged(boolean isInteractive);
+ }
+
+ private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+ public ScreenInteractiveHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+ * thread.
+ */
+ public final void addListener(ScreenInteractiveChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for changes to screen interactive state.
+ */
+ public final void removeListener(ScreenInteractiveChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ protected final void notifyScreenInteractiveChanged(boolean interactive) {
+ for (ScreenInteractiveChangedListener listener : mListeners) {
+ listener.onScreenInteractiveChanged(interactive);
+ }
+ }
+
+ /**
+ * Returns true if the screen is currently interactive, and false otherwise.
+ */
+ public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index a95383695ae8..cfb7697a8dfc 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -57,7 +57,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -75,7 +75,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -91,7 +91,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -106,7 +106,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
@@ -123,7 +123,7 @@ public class SystemAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
Preconditions.checkState(mAppOps != null);
long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 000000000000..b9c0ddef04ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final Context mContext;
+
+ private boolean mInited;
+
+ public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+ super(appOps);
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mInited) {
+ return;
+ }
+
+ mContext.getPackageManager().addOnPermissionsChangeListener(
+ uid -> {
+ // invoked on ui thread, move to fg thread so ui thread isn't blocked
+ FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+ });
+ mInited = true;
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mContext.checkPermission(permission, callerIdentity.getPid(),
+ callerIdentity.getUid()) == PERMISSION_GRANTED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..c8d8202157f0
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+ Consumer<PowerSaveState> {
+
+ private final Context mContext;
+ private boolean mReady;
+
+ @LocationPowerSaveMode
+ private volatile int mLocationPowerSaveMode;
+
+ public SystemLocationPowerSaveModeHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+ PowerManager.ServiceType.LOCATION, this);
+ mLocationPowerSaveMode = Objects.requireNonNull(
+ mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+ mReady = true;
+ }
+
+ @Override
+ public void accept(PowerSaveState powerSaveState) {
+ int locationPowerSaveMode;
+ if (!powerSaveState.batterySaverEnabled) {
+ locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ } else {
+ locationPowerSaveMode = powerSaveState.locationMode;
+ }
+
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+
+ // invoked on ui thread, move to fg thread so we don't block the ui thread
+ FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ Preconditions.checkState(mReady);
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 000000000000..70cedac20868
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private final Context mContext;
+
+ private boolean mReady;
+
+ private volatile boolean mIsInteractive;
+
+ public SystemScreenInteractiveHelper(Context context) {
+ mContext = context;
+ }
+
+ /** Called when system is ready. */
+ public void onSystemReady() {
+ if (mReady) {
+ return;
+ }
+
+ IntentFilter screenIntentFilter = new IntentFilter();
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean interactive;
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ interactive = true;
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ interactive = false;
+ } else {
+ return;
+ }
+
+ onScreenInteractiveChanged(interactive);
+ }
+ }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+ mReady = true;
+ }
+
+ private void onScreenInteractiveChanged(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ @Override
+ public boolean isInteractive() {
+ Preconditions.checkState(mReady);
+ return mIsInteractive;
+ }
+}
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5b23ea..2e2d812c058e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@ elaurent@google.com
hdmoon@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
klhyun@google.com
lajos@google.com
sungsoo@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00cb22e9d4b5..391a08db6716 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -459,12 +459,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final int returnCode = args.argi1;
args.recycle();
- final boolean showNotification;
- synchronized (mLock) {
- showNotification = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
- showNotification, userId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
@@ -494,8 +490,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
- @GuardedBy("mLock")
- private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+ private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
+ assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
+ assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
if (userId != UserHandle.getUserId(mInstallerUid)) {
return false;
}
@@ -513,12 +512,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
- @GuardedBy("mLock")
- private boolean needToAskForPermissionsLocked() {
- if (mPermissionsManuallyAccepted) {
- return false;
+ private boolean needToAskForPermissions() {
+ final String packageName;
+ synchronized (mLock) {
+ if (mPermissionsManuallyAccepted) {
+ return false;
+ }
+ packageName = mPackageName;
}
+ // It is safe to access mInstallerUid and mInstallSource without lock
+ // because they are immutable after sealing.
final boolean isInstallPermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
@@ -528,7 +532,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
+ final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && targetPackageUid != -1)
|| (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
@@ -540,7 +544,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+ || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -740,6 +744,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private void assertNotLocked(String cookie) {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(cookie + " is holding mLock");
+ }
+ }
+
+ private void assertSealed(String cookie) {
+ if (!isSealed()) {
+ throw new IllegalStateException(cookie + " before sealing");
+ }
+ }
+
@GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1693,11 +1709,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleInstall() {
- final boolean needsLogging;
- synchronized (mLock) {
- needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
- if (needsLogging) {
+ if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1724,9 +1736,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
- synchronized (mLock) {
- installNonStagedLocked(childSessions);
- }
+ installNonStaged(childSessions);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1735,11 +1745,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- @GuardedBy("mLock")
- private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
+ private void installNonStaged(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
- makeSessionActiveLocked();
+ makeSessionActive();
if (installingSession == null) {
return;
}
@@ -1752,7 +1761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActiveLocked();
+ session.makeSessionActive();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1762,8 +1771,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
@@ -1778,41 +1791,58 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
* in case permissions need to be requested before install can proceed.
*/
- @GuardedBy("mLock")
- private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ private PackageManagerService.ActiveInstallSession makeSessionActive()
throws PackageManagerException {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ assertNotLocked("makeSessionActive");
+
+ synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
}
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
+
+ if (!params.isMultiPackage && needToAskForPermissions()) {
+ // User needs to confirm installation;
+ // give installer an intent they can use to involve
+ // user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+ intent.setPackage(mPm.getPackageInstallerPackageName());
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ closeInternal(false);
+ return null;
}
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
+
+ synchronized (mLock) {
+ return makeSessionActiveLocked();
}
+ }
+ @GuardedBy("mLock")
+ private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ throws PackageManagerException {
if (!params.isMultiPackage) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
- if (needToAskForPermissionsLocked()) {
- // User needs to confirm installation;
- // give installer an intent they can use to involve
- // user.
- final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
- intent.setPackage(mPm.getPackageInstallerPackageName());
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
- sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
-
- // Commit was keeping session marked as active until now; release
- // that extra refcount so session appears idle.
- closeInternal(false);
- return null;
- }
-
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -2438,7 +2468,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Determine if creating hard links between source and destination is
* possible. That is, do they all live on the same underlying device.
*/
- private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+ private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
try {
final StructStat toStat = Os.stat(toDir.getAbsolutePath());
for (File fromFile : fromFiles) {
@@ -2915,7 +2945,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- sendPendingStreaming("DataLoader unavailable");
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId,
+ "DataLoader unavailable");
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
@@ -2929,7 +2964,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- sendPendingStreaming(e.getMessage());
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
}
}
};
@@ -3004,16 +3043,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
detailMessage).sendToTarget();
}
+ @GuardedBy("mLock")
+ private int[] getChildSessionIdsLocked() {
+ final int[] childSessionIds = mChildSessionIds.copyKeys();
+ return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+ }
+
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds;
synchronized (mLock) {
- childSessionIds = mChildSessionIds.copyKeys();
+ return getChildSessionIdsLocked();
}
- if (childSessionIds != null) {
- return childSessionIds;
- }
- return EMPTY_CHILD_SESSION_ARRAY;
}
private boolean canBeAddedAsChild(int parentCandidate) {
@@ -3323,6 +3363,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnUserActionRequired(Context context, IntentSender target,
int sessionId, Intent intent) {
final Intent fillIn = new Intent();
@@ -3335,6 +3378,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
boolean showNotification, int userId, String basePackageName, int returnCode,
String msg, Bundle extras) {
@@ -3375,13 +3421,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private void sendPendingStreaming(@Nullable String cause) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
-
- if (statusReceiver == null) {
+ /**
+ * This method doesn't change internal states and is safe to call outside the lock.
+ */
+ private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ @Nullable String cause) {
+ if (target == null) {
Slog.e(TAG, "Missing receiver for pending streaming status.");
return;
}
@@ -3396,7 +3441,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
}
try {
- statusReceiver.sendIntent(mContext, 0, intent, null, null);
+ target.sendIntent(context, 0, intent, null, null);
} catch (IntentSender.SendIntentException ignored) {
}
}
@@ -3470,10 +3515,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageCid != null) {
writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
}
- writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
- writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
- writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
- writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+ writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
+ writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted);
+ writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
+ writeBooleanAttribute(out, ATTR_SEALED, mSealed);
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
@@ -3535,7 +3580,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.appIconLastModified = appIconFile.lastModified();
}
- final int[] childSessionIds = getChildSessionIds();
+ final int[] childSessionIds = getChildSessionIdsLocked();
for (int childSessionId : childSessionIds) {
out.startTag(null, TAG_CHILD_SESSION);
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
@@ -3543,7 +3588,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final InstallationFile[] files = getInstallationFilesLocked();
- for (InstallationFile file : getInstallationFilesLocked()) {
+ for (InstallationFile file : files) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bd12fd5f5d9a..2854b337fd29 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -651,6 +651,23 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
150857253;
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -2952,9 +2969,8 @@ public class PackageManagerService extends IPackageManager.Stub
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
- String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
- addBuiltInSharedLibraryLocked(entry.filename, name);
+ addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -10486,6 +10502,19 @@ public class PackageManagerService extends IPackageManager.Stub
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
+ // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
+ if (pkg.getTargetSdkVersion() > 30) {
+ if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ }
+ }
return usesLibraryInfos;
}
@@ -12177,15 +12206,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private boolean addBuiltInSharedLibraryLocked(String path, String name) {
- if (nonStaticSharedLibExistsLocked(name)) {
+ private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+ if (nonStaticSharedLibExistsLocked(entry.name)) {
return false;
}
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
- (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
- new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
- null, null);
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+ entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
+ entry.isNative);
commitSharedLibraryInfoLocked(libraryInfo);
return true;
@@ -21900,7 +21930,11 @@ public class PackageManagerService extends IPackageManager.Stub
pw.print(" -> ");
}
if (libraryInfo.getPath() != null) {
- pw.print(" (jar) ");
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
pw.print(libraryInfo.getPath());
} else {
pw.print(" (apk) ");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 13b927e7d9f4..7106499f9b56 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4784,6 +4784,23 @@ public final class Settings {
}
}
+ List<String> usesNativeLibraries = pkg.getUsesNativeLibraries();
+ if (usesNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesNativeLibraries:");
+ for (int i=0; i< usesNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(usesNativeLibraries.get(i));
+ }
+ }
+
+ List<String> usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries();
+ if (usesOptionalNativeLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesOptionalNativeLibraries:");
+ for (int i=0; i< usesOptionalNativeLibraries.size(); i++) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(usesOptionalNativeLibraries.get(i));
+ }
+ }
+
List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
if (usesLibraryFiles.size() > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index c9e0bb467ce4..39784cf32cea 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -252,6 +252,19 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka
@NonNull
List<String> getUsesOptionalLibraries();
+ /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+ @NonNull
+ List<String> getUsesNativeLibraries();
+
+ /**
+ * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+ * expected to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesNativeLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalNativeLibraries();
+
/**
* TODO(b/135203078): Move static library stuff to an inner data class
* @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2394bafc09de..fd3c1f97df8b 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1469,7 +1469,7 @@ public class TrustManagerService extends SystemService {
if (userId > 0) {
return userId;
} else {
- Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+ Log.w(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
return -100;
}
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index f5eed30a19bf..f5e1602ee6be 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -51,6 +51,7 @@ import android.app.AppGlobals;
import android.app.GrantedUriPermission;
import android.app.IUriGrantsManager;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -698,6 +699,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid, grantUri);
perm.initPersistedModes(modeFlags, createdTime);
+ mPmInternal.grantImplicitAccess(
+ targetUserId, null,
+ UserHandle.getAppId(targetUid),
+ pi.applicationInfo.uid,
+ false /* direct */);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -1171,6 +1177,9 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
// grant, we can skip generating any bookkeeping; when any advanced
// features have been requested, we proceed below to make sure the
// provider supports granting permissions
+ mPmInternal.grantImplicitAccess(
+ UserHandle.getUserId(targetUid), null,
+ UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
return -1;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc883d9..90f87b16e70d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
};
- private Runnable mTryToRebindRunnable = () -> {
- tryToRebind();
- };
+ private Runnable mTryToRebindRunnable = this::tryToRebind;
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
@@ -1295,14 +1293,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// a short time in the future, specifically to allow any pending package
// update message on this same looper thread to be processed.
if (!mWallpaper.wallpaperUpdating) {
- mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
1000);
}
}
}
}
- public void scheduleTimeoutLocked() {
+ private void scheduleTimeoutLocked() {
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private void processDisconnect(final ServiceConnection connection) {
+ private Runnable mDisconnectRunnable = () -> {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
// crashes and fall back to default if it continues to misbehave.
- if (connection == mWallpaper.connection) {
+ if (this == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
}
- }
+ };
/**
* Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperConnection.DisplayConnector::disconnectLocked);
wallpaper.connection.mService = null;
wallpaper.connection.mDisplayConnector.clear();
+
+ FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mDisconnectRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(
+ wallpaper.connection.mTryToRebindRunnable);
+
wallpaper.connection = null;
if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0cd7ffce2ed4..04b1edc3eede 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1446,7 +1446,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mService.deferWindowLayout();
try {
stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- stack.setBounds(null);
+ if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ stack.setBounds(null);
+ }
toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
// Follow on the workaround: activities are kept force hidden till the new windowing
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 751d0c8be24a..e84f040931bb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1288,11 +1288,11 @@ class ActivityStarter {
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
- // wasn't whitelisted to start an activity
+ // wasn't allowed to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") is persistent system process AND intent sender whitelisted "
+ + ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
@@ -1346,23 +1346,23 @@ class ActivityStarter {
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
- // caller, so that we can make the decision based on its foreground/whitelisted state.
+ // caller, so that we can make the decision based on its state.
int callerAppUid = callingUid;
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
- // don't abort if the callerApp or other processes of that uid are whitelisted in any way
+ // don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
- + callerApp.getPid() + ", uid = " + callerAppUid + ") is whitelisted");
+ + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
}
return false;
}
- // only if that one wasn't whitelisted, check the other ones
+ // only if that one wasn't allowed, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
@@ -1372,7 +1372,7 @@ class ActivityStarter {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
- + " from uid " + callerAppUid + " is whitelisted");
+ + " from uid " + callerAppUid + " is allowed");
}
return false;
}
@@ -1401,7 +1401,7 @@ class ActivityStarter {
+ "; isRealCallingUidPersistentSystemProcess: "
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: " + originatingPendingIntent
- + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "]");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 127e10a6966b..0b1f4d9e1613 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -490,6 +490,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private ActivityRecord mFixedRotationLaunchingApp;
+ /** The delay to avoid toggling the animation quickly. */
+ private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
private FixedRotationAnimationController mFixedRotationAnimationController;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
@@ -1524,10 +1526,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
- if (mFixedRotationAnimationController == null) {
- mFixedRotationAnimationController = new FixedRotationAnimationController(this);
- mFixedRotationAnimationController.hide();
- }
+ startFixedRotationAnimation(
+ // Delay the hide animation to avoid blinking by clicking navigation bar that
+ // may toggle fixed rotation in a short time.
+ r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
finishFixedRotationAnimationIfPossible();
@@ -1625,6 +1627,32 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ /**
+ * Starts the hide animation for the windows which will be rotated seamlessly.
+ *
+ * @return {@code true} if the animation is executed right now.
+ */
+ private boolean startFixedRotationAnimation(boolean shouldDebounce) {
+ if (shouldDebounce) {
+ mWmService.mH.postDelayed(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ if (mFixedRotationLaunchingApp != null
+ && startFixedRotationAnimation(false /* shouldDebounce */)) {
+ // Apply the transaction so the animation leash can take effect immediately.
+ getPendingTransaction().apply();
+ }
+ }
+ }, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
+ return false;
+ }
+ if (mFixedRotationAnimationController == null) {
+ mFixedRotationAnimationController = new FixedRotationAnimationController(this);
+ mFixedRotationAnimationController.hide();
+ return true;
+ }
+ return false;
+ }
+
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishFixedRotationAnimationIfPossible() {
final FixedRotationAnimationController controller = mFixedRotationAnimationController;
@@ -3528,18 +3556,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
== mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
- // Now, a special case -- if the last target's window is in the process of exiting, but
- // not removed, keep on the last target to avoid IME flicker.
- final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget);
- if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing()
- && !cur.isActivityTypeHome()) {
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "Not changing control while current window"
- + " is closing and not removed");
- }
- return cur;
- }
- // Otherwise, we just use the ime target as received from IME.
return mInputMethodInputTarget;
}
}
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c7cba77f6797..e2c07491db01 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.Task.TAG_VISIBILITY;
@@ -174,12 +172,7 @@ class EnsureActivitiesVisibleHelper {
}
final int windowingMode = mContiner.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // The visibility of tasks and the activities they contain in freeform stack are
- // determined individually unlike other stacks where the visibility or fullscreen
- // status of an activity in a previous task affects other.
- mBehindFullscreenActivity = !mContainerShouldBeVisible;
- } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+ if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
&& r.isRootOfTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 49fafbb3907c..e6e92ea9e489 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5394,12 +5394,9 @@ class Task extends WindowContainer<WindowContainer> {
mCurrentUser = userId;
super.switchUser(userId);
- forAllLeafTasks((t) -> {
- if (t.showToCurrentUser() && t != this) {
- mChildren.remove(t);
- mChildren.add(t);
- }
- }, true /* traverseTopToBottom */);
+ if (isLeafTask() && showToCurrentUser()) {
+ getParent().positionChildAt(POSITION_TOP, this, false /*includeParents*/);
+ }
}
void minimalResumeActivityLocked(ActivityRecord r) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8912d584213e..4c5ac937b988 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -353,7 +353,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
} else {
- throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+ throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
}
} else {
// Ugh, of course ActivityStack has its own special reorder logic...
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9a48154c7770..da9c7f3ea1b5 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -160,7 +160,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
- // Set to true if this process is currently temporarily whitelisted to start activities even if
+ // Set to true if this process is currently temporarily allowed to start activities even if
// it's not in the foreground
private volatile boolean mAllowBackgroundActivityStarts;
// Set of UIDs of clients currently bound to this process
@@ -454,7 +454,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
boolean areBackgroundActivityStartsAllowed() {
- // allow if the whitelisting flag was explicitly set
+ // allow if the flag was explicitly set
if (mAllowBackgroundActivityStarts) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1cbc95090bfd..9ab157210dbd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2194,9 +2194,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (dc.mInputMethodInputTarget == this) {
dc.setInputMethodInputTarget(null);
}
- if (dc.mInputMethodControlTarget == this) {
- dc.updateImeControlTarget();
- }
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6ca4f4..7ec819f13e96 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
+ /**
+ * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+ * affected user ID.
+ */
+ @GuardedBy("getLockObject()")
+ private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+ for (int userId : affectedUserIds) {
+ saveSettingsLocked(userId);
+ }
+ }
+
private void saveSettingsLocked(int userHandle) {
DevicePolicyData policy = getUserData(userHandle);
JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Updates a flag that tells us whether the user's password currently satisfies the
- * requirements set by all of the user's active admins. The flag is updated both in memory
- * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
- * be the correct one upon boot.
- * This should be called whenever the password or the admin policies have changed.
+ * requirements set by all of the user's active admins.
+ * This should be called whenever the password or the admin policies have changed. The caller
+ * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+ *
+ * @return the set of user IDs that have been affected
*/
@GuardedBy("getLockObject()")
- private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
metrics, userHandle, parent);
if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
- saveSettingsLocked(credentialOwner);
+ affectedUserIds.add(credentialOwner);
}
}
+ return affectedUserIds;
}
/**
@@ -6175,7 +6189,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void removeCaApprovalsIfNeeded(int userId) {
+ private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!isSecure) {
synchronized (getLockObject()) {
getUserData(userInfo.id).mAcceptedCaCertificates.clear();
- saveSettingsLocked(userInfo.id);
+ affectedUserIds.add(userInfo.id);
}
mCertificateMonitor.onCertificateApprovalsChanged(userId);
}
}
+ return affectedUserIds;
}
@Override
@@ -7458,42 +7474,45 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
DevicePolicyData policy = getUserData(userId);
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
synchronized (getLockObject()) {
policy.mFailedPasswordAttempts = 0;
- updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
- saveSettingsLocked(userId);
- updatePasswordExpirationsLocked(userId);
+ affectedUserIds.add(userId);
+ affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+ userId, /* parent */ false));
+ affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
// Send a broadcast to each profile using this password as its primary unlock.
sendAdminCommandForLockscreenPoliciesLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+ affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+ saveSettingsForUsersLocked(affectedUserIds);
}
- removeCaApprovalsIfNeeded(userId);
}
/**
* Called any time the device password is updated. Resets all password expiration clocks.
+ *
+ * @return the set of user IDs that have been affected
*/
- private void updatePasswordExpirationsLocked(int userHandle) {
- ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+ private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+ final ArraySet<Integer> affectedUserIds = new ArraySet<>();
List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
userHandle, /* parent */ false);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < admins.size(); i++) {
ActiveAdmin admin = admins.get(i);
if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
affectedUserIds.add(admin.getUserHandle().getIdentifier());
long timeout = admin.passwordExpirationTimeout;
- long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
- admin.passwordExpirationDate = expiration;
+ admin.passwordExpirationDate =
+ timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
}
}
- for (int affectedUserId : affectedUserIds) {
- saveSettingsLocked(affectedUserId);
- }
+ return affectedUserIds;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da7f9c9..e947e89c4f94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
@@ -68,20 +68,20 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+ public void finishOp(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
Preconditions.checkState(myAppOp.mStarted);
myAppOp.mStarted = false;
}
@Override
- protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
return myAppOp.mAllowed;
}
@Override
- protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
if (!noteOpNoThrow(appOp, callerIdentity)) {
throw new SecurityException(
"noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
}
@Override
- protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+ public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
if (!myAppOp.mAllowed) {
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 000000000000..e7d7e310d1e4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+ private final HashMap<String, Set<String>> mRevokedPermissions;
+
+ public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+ super(appOps);
+ mRevokedPermissions = new HashMap<>();
+ }
+
+ public void grantPermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).remove(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ public void revokePermission(String packageName, String permission) {
+ getRevokedPermissionsList(packageName).add(permission);
+ notifyLocationPermissionsChanged(packageName);
+ }
+
+ @Override
+ protected boolean hasPermission(String permission, CallerIdentity identity) {
+ return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+ }
+
+ private Set<String> getRevokedPermissionsList(String packageName) {
+ return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 000000000000..3ead5d4f214d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+ @LocationPowerSaveMode
+ private int mLocationPowerSaveMode;
+
+ public FakeLocationPowerSaveModeHelper() {
+ mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+ }
+
+ public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+ if (locationPowerSaveMode == mLocationPowerSaveMode) {
+ return;
+ }
+
+ mLocationPowerSaveMode = locationPowerSaveMode;
+ notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+ }
+
+ @LocationPowerSaveMode
+ @Override
+ public int getLocationPowerSaveMode() {
+ return mLocationPowerSaveMode;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 000000000000..df697fa1a03c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+ private boolean mIsInteractive;
+
+ public FakeScreenInteractiveHelper() {
+ mIsInteractive = true;
+ }
+
+ public void setScreenInteractive(boolean interactive) {
+ if (interactive == mIsInteractive) {
+ return;
+ }
+
+ mIsInteractive = interactive;
+ notifyScreenInteractiveChanged(interactive);
+ }
+
+ public boolean isInteractive() {
+ return mIsInteractive;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b82b699..1d0523f18008 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@ public class FakeSettingsHelper extends SettingsHelper {
@Override
public boolean isLocationEnabled(int userId) {
- return mLocationEnabledSetting.getValue(Boolean.class);
+ return mLocationEnabledSetting.getValue(userId, Boolean.class);
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c879ba..f5978da416be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
package com.android.server.location.util;
-import android.os.Process;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.SparseArray;
@@ -27,19 +26,21 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
/**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
*/
public class FakeUserInfoHelper extends UserInfoHelper {
+ public static final int DEFAULT_USERID = 0;
+
private final IntArray mRunningUserIds;
private final SparseArray<IntArray> mProfiles;
private int mCurrentUserId;
public FakeUserInfoHelper() {
- mCurrentUserId = Process.myUserHandle().getIdentifier();
- mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+ mCurrentUserId = DEFAULT_USERID;
+ mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
mProfiles = new SparseArray<>();
}
@@ -67,6 +68,10 @@ public class FakeUserInfoHelper extends UserInfoHelper {
dispatchOnUserStopped(userId);
}
+ public void setCurrentUserId(int parentUser) {
+ setCurrentUserIds(parentUser, new int[]{parentUser});
+ }
+
public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f625217965..4165b6ee111f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
package com.android.server.location.util;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@ public class LocationAttributionHelperTest {
public void setUp() {
initMocks(this);
- when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
- when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
- true);
+ when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
mHelper = new LocationAttributionHelper(mAppOpsHelper);
}
@@ -63,30 +65,30 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
}
@Test
@@ -99,29 +101,29 @@ public class LocationAttributionHelperTest {
Object key4 = new Object();
mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index f40d3168cf98..093aa2e0e771 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@ import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -34,10 +30,12 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
import android.app.AppOpsManager;
import android.content.Context;
@@ -105,41 +103,41 @@ public class SystemAppOpsHelperTest {
}
@Test
- public void testCheckLocationAccess() {
+ public void testCheckOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
- assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testNoteLocationAccess() {
+ public void testNoteOpNoThrow() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -147,74 +145,55 @@ public class SystemAppOpsHelperTest {
doReturn(MODE_ALLOWED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
eq("myfeature"), nullable(String.class));
- assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+ assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
}
@Test
- public void testStartLocationMonitoring() {
+ public void testStartOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+ assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
}
@Test
- public void testStartHighPowerLocationMonitoring() {
+ public void testFinishOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
- doReturn(MODE_ALLOWED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
- doReturn(MODE_IGNORED).when(
- mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
- eq("mypackage"),
- eq(false), eq("myfeature"), nullable(String.class));
- assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
- }
-
- @Test
- public void testStopLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopLocationMonitoring(identity);
+ mHelper.finishOp(OP_MONITOR_LOCATION, identity);
verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
}
@Test
- public void testStopHighPowerLocationMonitoring() {
- CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
- mHelper.stopHighPowerLocationMonitoring(identity);
- verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
- }
-
- @Test
- public void testNoteMockLocationAccess() {
+ public void testNoteOp() {
CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
doReturn(MODE_ALLOWED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
doReturn(MODE_IGNORED).when(
mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
nullable(String.class));
- assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+ assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+ doThrow(new SecurityException()).when(
+ mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+ nullable(String.class));
+ assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 000000000000..2acb70c4b83d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+ private static final long TIMEOUT_MS = 5000;
+ private static final long FAILURE_TIMEOUT_MS = 200;
+
+ @Mock
+ private PowerManagerInternal mPowerManagerInternal;
+
+ private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+ private SystemLocationPowerSaveModeHelper mHelper;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+ doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+ mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+ PowerManager powerManager = mock(PowerManager.class);
+ doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+ Context context = mock(Context.class);
+ doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+ mHelper = new SystemLocationPowerSaveModeHelper(context);
+ mHelper.onSystemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ }
+
+ private void sendPowerSaveState(PowerSaveState powerSaveState) {
+ for (Consumer<PowerSaveState> listener : mListeners) {
+ listener.accept(powerSaveState);
+ }
+ }
+
+ @Test
+ public void testListener() {
+ LocationPowerSaveModeChangedListener listener = mock(
+ LocationPowerSaveModeChangedListener.class);
+ mHelper.addListener(listener);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ false).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+ anyInt());
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_FOREGROUND_ONLY);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+ true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+ LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+ sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+ LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+ verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+ LOCATION_MODE_NO_CHANGE);
+ assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc104f438..1867be0b9f3b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
package com.android.server.location.util;
+import com.android.server.location.LocationRequestStatistics;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAppOpsHelper mAppOpsHelper;
+ private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
private final FakeSettingsHelper mSettingsHelper;
private final FakeAppForegroundHelper mAppForegroundHelper;
- private final LocationUsageLogger mLocationUsageLogger;
+ private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+ private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
+ private final LocationUsageLogger mLocationUsageLogger;
+ private final LocationRequestStatistics mLocationRequestStatistics;
public TestInjector() {
mUserInfoHelper = new FakeUserInfoHelper();
mAppOpsHelper = new FakeAppOpsHelper();
+ mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationUsageLogger = new LocationUsageLogger();
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+ mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+ mLocationUsageLogger = new LocationUsageLogger();
+ mLocationRequestStatistics = new LocationRequestStatistics();
}
@Override
@@ -45,6 +55,11 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public FakeSettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -55,12 +70,27 @@ public class TestInjector implements Injector {
}
@Override
- public LocationUsageLogger getLocationUsageLogger() {
- return mLocationUsageLogger;
+ public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+ return mLocationPowerSaveModeHelper;
+ }
+
+ @Override
+ public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+ return mScreenInteractiveHelper;
}
@Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
+
+ @Override
+ public LocationUsageLogger getLocationUsageLogger() {
+ return mLocationUsageLogger;
+ }
+
+ @Override
+ public LocationRequestStatistics getLocationRequestStatistics() {
+ return mLocationRequestStatistics;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 4fbc587c8a87..6f37ff5ef329 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -153,6 +153,20 @@ public class GestureLauncherServiceTest {
}
@Test
+ public void testIsPanicButtonGestureEnabled_settingDisabled() {
+ withPanicGestureEnabledSettingValue(false);
+ assertFalse(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
+ public void testIsPanicButtonGestureEnabled_settingEnabled() {
+ withPanicGestureEnabledSettingValue(true);
+ assertTrue(mGestureLauncherService.isPanicButtonGestureEnabled(
+ mContext, FAKE_USER_ID));
+ }
+
+ @Test
public void testHandleCameraLaunchGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
@@ -882,6 +896,14 @@ public class GestureLauncherServiceTest {
UserHandle.USER_CURRENT);
}
+ private void withPanicGestureEnabledSettingValue(boolean enable) {
+ Settings.Secure.putIntForUser(
+ mContentResolver,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
+ enable ? 1 : 0,
+ UserHandle.USER_CURRENT);
+ }
+
private void withUserSetupCompleteValue(boolean userSetupComplete) {
int userSetupCompleteValue = userSetupComplete ? 1 : 0;
Settings.Secure.putIntForUser(
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 daaabf8141ff..9a465a91e84e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(passwordMetrics);
dpm.reportPasswordChanged(userHandle);
- // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
- // reportPasswordChanged()
- // This broadcast should be sent 2-4 times:
- // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
- // once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
- // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
- // in DevicePolicyManagerService.reportPasswordChanged, once with the userId
- // the password change is relevant to and another with the credential owner of said
- // userId, if the password checkpoint value changes.
- verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
- MockUtils.checkIntentAction(
- DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(userHandle));
- verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 62b6a65cc6cb..614949c91b9a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -43,11 +43,19 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.UserHandle;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +70,12 @@ public class UriGrantsManagerServiceTest {
private UriGrantsMockContext mContext;
private UriGrantsManagerInternal mService;
+ // we expect the following only during grant if a grant is expected
+ private void verifyNoVisibilityGrant() {
+ verify(mContext.mPmInternal, never())
+ .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+ }
+
@Before
public void setUp() throws Exception {
mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
@@ -83,6 +97,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -100,6 +115,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
@@ -111,6 +127,8 @@ public class UriGrantsManagerServiceTest {
final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
assertNull(needed);
+ verify(mContext.mPmInternal).grantImplicitAccess(eq(USER_PRIMARY), isNull(), eq(
+ UserHandle.getAppId(UID_PRIMARY_SOCIAL)), eq(UID_PRIMARY_PUBLIC), eq(false));
}
/**
@@ -128,6 +146,7 @@ public class UriGrantsManagerServiceTest {
assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
assertEquals(FLAG_READ, needed.flags);
assertEquals(asSet(expectedGrant), needed.uris);
+ verifyNoVisibilityGrant();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f2f8a12b6ea9..3772e2500c1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -618,7 +618,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, true, false, false, false);
runAndVerifyBackgroundActivityStartsSubtest(
- "disallowed_callerIsWhitelisted_notAborted", false,
+ "disallowed_callerIsAllowed_notAborted", false,
UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, true, false, false);
@@ -639,7 +639,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
boolean hasForegroundActivities, boolean callerIsRecents,
- boolean callerIsTempWhitelisted,
+ boolean callerIsTempAllowed,
boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
boolean isCallingUidDeviceOwner) {
// window visibility
@@ -664,8 +664,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
RecentTasks recentTasks = mock(RecentTasks.class);
mService.mStackSupervisor.setRecentTasks(recentTasks);
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
- // caller is temp whitelisted
- callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
+ // caller is temp allowed
+ callerApp.setAllowBackgroundActivityStarts(callerIsTempAllowed);
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
callerIsInstrumentingWithBackgroundActivityStartPrivileges);
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 a7e0dd4e674e..96ea64667f6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -974,26 +974,6 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
- public void testComputeImeControlTarget_exitingApp() throws Exception {
- final DisplayContent dc = createNewDisplay();
-
- WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app");
- makeWindowVisible(exitingWin);
- exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
- exitingWin.mAnimatingExit = true;
-
- dc.mInputMethodControlTarget = exitingWin;
- dc.mInputMethodTarget = dc.mInputMethodInputTarget =
- createWindow(null, TYPE_BASE_APPLICATION, "starting app");
-
- assertEquals(exitingWin, dc.computeImeControlTarget());
-
- exitingWin.removeImmediately();
-
- assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
- }
-
- @Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
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 039ffc464337..92b6e6ef8ec9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -186,4 +186,19 @@ public class TaskTests extends WindowTestsBase {
assertTrue(r.finishing);
});
}
+
+ @Test
+ public void testSwitchUser() {
+ final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
+ final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
+ final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
+ final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask2, childTask.getTopChild());
+
+ doReturn(true).when(leafTask1).showToCurrentUser();
+ rootTask.switchUser(10);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask1, childTask.getTopChild());
+ }
}
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
index 597805451b95..61dd9d4cd021 100644
--- a/tests/WindowInsetsTests/AndroidManifest.xml
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.windowinsetstests">
-
- <application android:label="@string/activity_title">
- <activity android:name=".WindowInsetsActivity"
- android:theme="@style/appTheme"
- android:windowSoftInputMode="adjustResize"
- android:exported="true">
+ package="com.google.android.test.windowinsetstests">
+ <application android:label="@string/application_title">
+ <activity android:name=".WindowInsetsTestsMainActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name=".ChatActivity"
+ android:label="@string/chat_activity_title"
+ android:theme="@style/chat"
+ android:windowSoftInputMode="adjustResize" />
+
+ <activity android:name=".ControllerActivity"
+ android:label="@string/controller_activity_title"
+ android:theme="@style/controller" />
</application>
</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/chat_activity.xml
index 1b51c4f83fe0..1b51c4f83fe0 100644
--- a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
+++ b/tests/WindowInsetsTests/res/layout/chat_activity.xml
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
new file mode 100644
index 000000000000..d51a4ddd43e8
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Spinner
+ android:id="@+id/spinnerBehavior"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Status Bars Toggle Button"
+ android:textOff="Status Bars Invisible"
+ android:textOn="Status Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Navigation Bars Toggle Button"
+ android:textOff="Navigation Bars Invisible"
+ android:textOn="Navigation Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="IME Toggle Button"
+ android:textOff="IME Invisible"
+ android:textOn="IME Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="0" />
+
+ <TextView
+ android:id="@+id/textViewControllableInsets"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp" />
+
+ <EditText
+ android:id="@+id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="For testing IME..."
+ android:inputType="text"
+ android:text="" />
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/layout/main_activity.xml b/tests/WindowInsetsTests/res/layout/main_activity.xml
new file mode 100644
index 000000000000..621ed89204d1
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/main_activity.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/chat_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/chat_activity_title"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/controller_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/controller_activity_title"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index 2b8e5f3da362..1a236c6f751d 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -16,5 +16,14 @@
-->
<resources>
- <string name="activity_title">New Insets Chat</string>
+ <string name="application_title">Window Insets Tests</string>
+ <string name="chat_activity_title">New Insets Chat</string>
+ <string name="controller_activity_title">Window Insets Controller</string>
+
+ <!-- The item positions should match the flag values respectively. -->
+ <string-array name="behaviors">
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item>
+ <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
+ </string-array>
</resources>
diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml
index 220671fb8e71..a84ffbed600d 100644
--- a/tests/WindowInsetsTests/res/values/styles.xml
+++ b/tests/WindowInsetsTests/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
- <style name="appTheme" parent="@style/Theme.MaterialComponents.Light">
+ <style name="chat" parent="@style/Theme.MaterialComponents.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
@@ -63,5 +63,11 @@
<dimen name="bubble_padding">8dp</dimen>
<dimen name="bubble_padding_side">16dp</dimen>
+ <style name="controller" parent="android:Theme.Material">
+ <item name="android:colorPrimaryDark">#111111</item>
+ <item name="android:navigationBarColor">#111111</item>
+ <item name="android:colorPrimary">#222222</item>
+ <item name="android:colorAccent">#33ccff</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index 498cb7c1c710..ba12acb2c877 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -30,7 +30,6 @@ import android.content.Context;
import android.graphics.Insets;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -39,8 +38,6 @@ import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.WindowInsetsController;
-import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
@@ -49,7 +46,7 @@ import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
-public class WindowInsetsActivity extends AppCompatActivity {
+public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -58,7 +55,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.window_inset_activity);
+ setContentView(R.layout.chat_activity);
setSupportActionBar(findViewById(R.id.toolbar));
@@ -71,7 +68,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
mRoot.setOnTouchListener(new View.OnTouchListener() {
private final ViewConfiguration mViewConfiguration =
- ViewConfiguration.get(WindowInsetsActivity.this);
+ ViewConfiguration.get(ChatActivity.this);
WindowInsetsAnimationController mAnimationController;
WindowInsetsAnimationControlListener mCurrentRequest;
boolean mRequestedController = false;
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
new file mode 100644
index 000000000000..beb4049cb230
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener {
+
+ private ToggleButton mToggleStatus;
+ private SeekBar mSeekStatus;
+ private ToggleButton mToggleNavigation;
+ private SeekBar mSeekNavigation;
+ private ToggleButton mToggleIme;
+ private SeekBar mSeekIme;
+ private TextView mTextControllableInsets;
+ private boolean[] mNotFromUser = {false};
+ private WindowInsets mLastInsets;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.controller_activity);
+ final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior);
+ ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this,
+ R.array.behaviors, android.R.layout.simple_spinner_item);
+ adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerBehavior.setAdapter(adapterBehavior);
+ spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ parent.getWindowInsetsController().setSystemBarsBehavior(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) { }
+ });
+ mToggleStatus = findViewById(R.id.toggleButtonStatus);
+ mToggleStatus.setTag(mNotFromUser);
+ mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars()));
+ mSeekStatus = findViewById(R.id.seekBarStatus);
+ mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars()));
+ mToggleNavigation = findViewById(R.id.toggleButtonNavigation);
+ mToggleNavigation.setTag(mNotFromUser);
+ mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars()));
+ mSeekNavigation = findViewById(R.id.seekBarNavigation);
+ mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars()));
+ mToggleIme = findViewById(R.id.toggleButtonIme);
+ mToggleIme.setTag(mNotFromUser);
+ mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime()));
+ mSeekIme = findViewById(R.id.seekBarIme);
+ mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime()));
+ mTextControllableInsets = findViewById(R.id.textViewControllableInsets);
+ final View contentView = findViewById(R.id.content);
+ contentView.setOnApplyWindowInsetsListener(this);
+ contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
+ (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mNotFromUser[0] = true;
+ updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus);
+ updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation);
+ updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme);
+ mLastInsets = insets;
+ mNotFromUser[0] = false;
+
+ // Prevent triggering system gestures while controlling seek bars.
+ final Insets gestureInsets = insets.getInsets(Type.systemGestures());
+ v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0);
+
+ return v.onApplyWindowInsets(insets);
+ }
+
+ private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) {
+ final boolean isVisible = insets.isVisible(types);
+ final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible;
+ if (isVisible != wasVisible) {
+ toggle.setChecked(isVisible);
+ if (!seek.isPressed()) {
+ seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/);
+ }
+ }
+
+ }
+
+ private static class ToggleListener implements CompoundButton.OnCheckedChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ ToggleListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (((boolean[]) buttonView.getTag())[0]) {
+ // not from user
+ return;
+ }
+ if (isChecked) {
+ buttonView.getWindowInsetsController().show(mTypes);
+ } else {
+ buttonView.getWindowInsetsController().hide(mTypes);
+ }
+ }
+ }
+
+ private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ private WindowInsetsAnimationController mController;
+
+ SeekBarListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mController != null && fromUser) {
+ final int min = seekBar.getMin();
+ final float fraction = (progress - min) / (float) (seekBar.getMax() - min);
+ final Insets shownInsets = mController.getShownStateInsets();
+ final Insets hiddenInsets = mController.getHiddenStateInsets();
+ final Insets currentInsets = Insets.of(
+ (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)),
+ (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)),
+ (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)),
+ (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom)));
+ mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mController != null) {
+ return;
+ }
+ seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes,
+ -1 /* durationMs */, null /* interpolator */, null /* cancellationSignal */,
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ mController = controller;
+ if (!seekBar.isPressed()) {
+ onStopTrackingTouch(seekBar);
+ }
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+ });
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ final int min = seekBar.getMin();
+ final int max = seekBar.getMax();
+ final boolean shown = (seekBar.getProgress() - min) * 2 > max - min;
+ seekBar.setProgress(shown ? max : min);
+ if (mController != null) {
+ mController.finish(shown);
+ }
+ }
+ }
+}
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
new file mode 100644
index 000000000000..8b77a78ff51e
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class WindowInsetsTestsMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_activity);
+
+ findViewById(R.id.chat_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ChatActivity.class)));
+
+ findViewById(R.id.controller_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ControllerActivity.class)));
+ }
+}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 32686538c10d..ff54fccda767 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
- std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
}
}
- return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
+ const Source res_path = options.source_path
+ ? StringPiece(options.source_path.value())
+ : StringPiece(path);
+
+ return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
extension.to_string(), config_str.to_string(), config};
}
@@ -667,7 +673,8 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter*
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(
+ path, inputs->GetDirSeparator(), &err_str, options)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage()
<< "only one of --dir and --zip can be specified");
return 1;
+ } else if ((options_.res_dir || options_.res_zip) &&
+ options_.source_path && args.size() > 1) {
+ context.GetDiagnostics()->Error(DiagMessage(kPath)
+ << "Cannot use an overriding source path with multiple files.");
+ return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1adac24..1bc1f6651f85 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
+ Maybe<std::string> source_path;
Maybe<std::string> res_dir;
Maybe<std::string> res_zip;
Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@ class CompileCommand : public Command {
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalFlag("--source-path",
+ "Sets the compiled resource file source file path to the given string.",
+ &options_.source_path);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a31360e..0aab94d3299f 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -253,4 +254,90 @@ TEST_F(CompilerTest, DoNotTranslateTest) {
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
+TEST_F(CompilerTest, RelativePathTest) {
+ StdErrDiagnostics diag;
+ const std::string res_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
+
+ const std::string path_values_colors = GetTestPath("values/colors.xml");
+ WriteFile(path_values_colors, "<resources>"
+ "<color name=\"color_one\">#008577</color>"
+ "</resources>");
+
+ const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+ WriteFile(path_layout_layout_one, "<LinearLayout "
+ "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+ "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+ "</LinearLayout>");
+
+ const std::string compiled_files_dir = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "compiled"});
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ const std::string path_values_colors_out =
+ BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+ const std::string path_layout_layout_one_out =
+ BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+ ::android::base::utf8::unlink(path_values_colors_out.c_str());
+ ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+ const std::string apk_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "out.apk"});
+
+ const std::string source_set_res = BuildPath({"main", "res"});
+ const std::string relative_path_values_colors =
+ BuildPath({source_set_res, "values", "colors.xml"});
+ const std::string relative_path_layout_layout_one =
+ BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+ CompileCommand(&diag).Execute({
+ path_values_colors,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_values_colors},
+ &std::cerr);
+
+ CompileCommand(&diag).Execute({
+ path_layout_layout_one,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_layout_layout_one},
+ &std::cerr);
+
+ std::ifstream ifs_values(path_values_colors_out);
+ std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+ (std::istreambuf_iterator<char>()));
+ ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+ ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+ Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+ compiled_files_dir, &diag);
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+ ResourceTable* resource_table = apk.get()->GetResourceTable();
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ resource_table->string_pool.strings();
+
+ ASSERT_EQ(pool_strings.size(), 2);
+ ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+ ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+ // Check resources.pb contains relative sources.
+ io::IFile* proto_file =
+ apk.get()->GetFileCollection()->FindFile("resources.pb");
+ std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+ io::ProtoInputStreamReader proto_reader(proto_stream.get());
+ pb::ResourceTable pb_table;
+ proto_reader.ReadMessage(&pb_table);
+
+ const std::string pool_strings_proto = pb_table.source_pool().data();
+
+ ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+ ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd031284..53d9ffe21949 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1405,6 +1405,29 @@ class UsesStaticLibrary : public ManifestExtractor::Element {
}
};
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+ UsesNativeLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!name.empty()) {
+ printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -2245,6 +2268,7 @@ T* ElementCast(ManifestExtractor::Element* element) {
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2295,6 +2319,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
{"uses-package", &CreateType<UsesPackage>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-native-library", &CreateType<UsesNativeLibrary>},
};
// Attempt to map the xml tag to a element inflater
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 3d69093a936a..49f8e1bcd30b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -422,6 +422,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action.Action(OptionalNameIsJavaClassName);
application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+ application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
application_action["library"].Action(RequiredNameIsNotEmpty);
application_action["profileable"];