summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp11
-rw-r--r--CleanSpec.mk1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java10
-rw-r--r--api/current.txt29
-rw-r--r--api/system-current.txt34
-rw-r--r--cmds/app_process/Android.bp1
-rw-r--r--cmds/statsd/Android.bp146
-rw-r--r--cmds/statsd/src/atoms.proto8
-rw-r--r--config/dirty-image-objects2
-rw-r--r--config/preloaded-classes2
-rw-r--r--config/preloaded-classes-blacklist1
-rw-r--r--core/java/android/app/Activity.java33
-rw-r--r--core/java/android/app/AppOpsManager.java68
-rw-r--r--core/java/android/app/assist/AssistStructure.java10
-rw-r--r--core/java/android/appwidget/AppWidgetHost.java32
-rw-r--r--core/java/android/appwidget/PendingHostUpdate.java8
-rw-r--r--core/java/android/bluetooth/BluetoothOutputStream.java12
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java14
-rw-r--r--core/java/android/content/IntentFilter.java4
-rw-r--r--core/java/android/content/pm/PackageParser.java50
-rw-r--r--core/java/android/content/pm/UserInfo.java2
-rw-r--r--core/java/android/content/res/Configuration.java11
-rw-r--r--core/java/android/hardware/Camera.java11
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java35
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java3
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java68
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java13
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java15
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java8
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java11
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java91
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl3
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl40
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java16
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl12
-rw-r--r--core/java/android/net/LocalSocketImpl.java34
-rw-r--r--core/java/android/os/LocaleList.java22
-rw-r--r--core/java/android/os/UpdateEngine.java5
-rw-r--r--core/java/android/os/UserManager.java2
-rw-r--r--core/java/android/os/VintfObject.java2
-rw-r--r--core/java/android/permission/IPermissionController.aidl1
-rw-r--r--core/java/android/permission/PermissionControllerManager.java12
-rw-r--r--core/java/android/permission/PermissionControllerService.java20
-rw-r--r--core/java/android/provider/CallLog.java18
-rw-r--r--core/java/android/provider/Settings.java20
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java9
-rw-r--r--core/java/android/util/TimeUtils.java11
-rw-r--r--core/java/android/view/IWindowManager.aidl10
-rw-r--r--core/java/android/view/IWindowSession.aidl4
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java9
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/InsetsSourceControl.java13
-rw-r--r--core/java/android/view/SurfaceControl.aidl1
-rw-r--r--core/java/android/view/Transaction.aidl19
-rw-r--r--core/java/android/view/View.java56
-rw-r--r--core/java/android/view/ViewParent.java1
-rw-r--r--core/java/android/view/WindowManager.java7
-rw-r--r--core/java/android/view/WindowlessWindowManager.java15
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java11
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java37
-rw-r--r--core/java/android/view/contentcapture/ViewNode.java14
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/android/widget/Magnifier.java13
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java162
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java22
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetHost.aidl1
-rw-r--r--core/java/com/android/internal/os/AtomicFile.java188
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java1
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java42
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java9
-rw-r--r--core/java/com/android/internal/policy/DecorView.java4
-rw-r--r--core/java/com/android/internal/widget/ExploreByTouchHelper.java1
-rw-r--r--core/jni/Android.bp5
-rw-r--r--core/jni/android_app_ActivityThread.cpp2
-rw-r--r--core/jni/android_ddm_DdmHandleNativeHeap.cpp2
-rw-r--r--core/jni/android_hardware_Camera.cpp25
-rw-r--r--core/jni/android_hardware_SoundTrigger.cpp10
-rw-r--r--core/jni/android_media_AudioTrack.cpp2
-rw-r--r--core/jni/android_os_Debug.cpp2
-rw-r--r--core/jni/android_os_HwBinder.cpp2
-rw-r--r--core/jni/android_view_InputChannel.cpp82
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp2
-rw-r--r--core/proto/android/content/configuration.proto3
-rw-r--r--core/proto/android/content/locale.proto1
-rw-r--r--core/proto/android/stats/docsui/docsui_enums.proto2
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/values-en-rCA/strings.xml9
-rw-r--r--core/res/res/values-en-rXC/strings.xml9
-rw-r--r--core/res/res/values-es-rUS/strings.xml4
-rw-r--r--core/res/res/values-sl/strings.xml2
-rw-r--r--core/res/res/values-ta/strings.xml10
-rw-r--r--core/res/res/values-th/strings.xml2
-rw-r--r--core/res/res/xml/sms_short_codes.xml2
-rw-r--r--core/tests/coretests/src/android/content/res/ConfigurationTest.java79
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java12
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java187
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java11
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java10
-rw-r--r--data/keyboards/Vendor_045e_Product_02dd.kl57
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp8
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp7
-rw-r--r--libs/hwui/renderthread/CacheManager.h1
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp6
-rw-r--r--libs/hwui/renderthread/ReliableSurface.cpp2
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h4
-rw-r--r--location/java/android/location/ILocationManager.aidl4
-rw-r--r--location/java/android/location/LocationManager.java29
-rw-r--r--location/java/com/android/internal/location/ILocationProvider.aidl6
-rw-r--r--location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java137
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java30
-rw-r--r--media/java/android/media/AudioTrack.java9
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_MediaDataSource.cpp3
-rw-r--r--media/jni/android_media_MediaDescrambler.cpp5
-rw-r--r--media/jni/android_media_MediaHTTPConnection.cpp2
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp38
-rw-r--r--media/jni/soundpool/SoundPool.cpp20
-rw-r--r--media/jni/soundpool/SoundPool.h2
-rw-r--r--media/native/midi/Android.bp2
-rw-r--r--media/tests/audiotests/shared_mem_test.cpp2
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java242
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java71
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java116
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java136
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java104
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java28
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java132
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java202
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java104
-rw-r--r--packages/BackupEncryption/test/robolectric/Android.bp6
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java212
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java127
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java179
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java173
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java200
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java131
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java213
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java113
-rw-r--r--packages/CarSystemUI/Android.bp43
-rw-r--r--packages/CarSystemUI/res/values/colors.xml2
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml2
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java1
-rw-r--r--packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hi/arrays.xml16
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-hy/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java12
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/res/values-zh-rTW/strings.xml12
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java21
-rw-r--r--packages/SystemUI/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java28
-rw-r--r--packages/SystemUI/res-keyguard/values-cs/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-fa/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable/biometric_dialog_bg.xml26
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml26
-rw-r--r--packages/SystemUI/res/layout/auth_container_view.xml3
-rw-r--r--packages/SystemUI/res/layout/biometric_dialog.xml190
-rw-r--r--packages/SystemUI/res/layout/keyguard_indication_text_view.xml11
-rw-r--r--packages/SystemUI/res/layout/qs_carrier_group.xml11
-rw-r--r--packages/SystemUI/res/layout/qs_tile_detail_text.xml33
-rw-r--r--packages/SystemUI/res/values-be/strings.xml4
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml22
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml2
-rw-r--r--packages/SystemUI/res/values-da/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml2
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml2
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml2
-rw-r--r--packages/SystemUI/res/values-night/colors.xml5
-rw-r--r--packages/SystemUI/res/values-or/strings.xml6
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml2
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml14
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierTextController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java121
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistModule.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java147
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java963
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java252
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/Utils.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java177
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java186
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java250
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java201
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java209
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java177
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java279
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java125
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java20
-rw-r--r--packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml2
-rw-r--r--proto/src/gnss.proto21
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java27
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java14
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java70
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java2
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java108
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java90
-rw-r--r--services/core/java/com/android/server/RecoverySystemService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/MemoryStatUtil.java131
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java298
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java23
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java132
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java140
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java23
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java45
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java17
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java30
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java36
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java150
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java193
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java108
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java54
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java19
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java83
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java9
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java4
-rw-r--r--services/core/java/com/android/server/policy/WindowOrientationListener.java8
-rw-r--r--services/core/java/com/android/server/power/AttentionDetector.java7
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java132
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java66
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java21
-rw-r--r--services/core/java/com/android/server/stats/IonMemoryUtil.java167
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java36
-rw-r--r--services/core/java/com/android/server/storage/CacheQuotaStrategy.java2
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderProxy.java126
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java16
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteService.java142
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java64
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityDisplay.java27
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java44
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java79
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java33
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java3
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java27
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java61
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java8
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java4
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java7
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java8
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java5
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java33
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java5
-rw-r--r--services/core/java/com/android/server/wm/Session.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java107
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java48
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java48
-rw-r--r--services/core/jni/Android.bp7
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp2
-rw-r--r--services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java6
-rw-r--r--services/tests/mockingservicestests/Android.bp1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java364
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java77
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java158
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java145
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java94
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java37
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java7
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java315
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java72
-rw-r--r--services/usb/java/com/android/server/usb/UsbSettingsManager.java2
-rw-r--r--startop/apps/test/Android.bp2
-rwxr-xr-xstartop/scripts/app_startup/app_startup_runner.py48
-rwxr-xr-xstartop/scripts/app_startup/app_startup_runner_test.py2
-rw-r--r--[-rwxr-xr-x]startop/scripts/iorap/compiler.py356
-rw-r--r--startop/scripts/iorap/compiler_device.py68
-rwxr-xr-xstartop/scripts/iorap/compiler_ri.py325
-rw-r--r--startop/scripts/iorap/compiler_test.py2
-rw-r--r--startop/scripts/iorap/lib/iorapd_utils.py21
-rw-r--r--telephony/java/android/telephony/CallerInfo.java (renamed from telephony/java/com/android/internal/telephony/CallerInfo.java)124
-rw-r--r--telephony/java/android/telephony/CallerInfoAsyncQuery.java (renamed from telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java)11
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java23
-rw-r--r--telephony/java/android/telephony/ModemActivityInfo.java171
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java64
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl5
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java75
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rw-r--r--tests/BootImageProfileTest/Android.bp20
-rw-r--r--tests/BootImageProfileTest/AndroidTest.xml39
-rw-r--r--tests/BootImageProfileTest/TEST_MAPPING7
-rw-r--r--tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java88
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleDataClass.java6
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java6
-rw-r--r--tests/FlickerTests/lib/Android.bp15
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java38
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java6
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java2
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java76
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java3
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java91
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java24
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java16
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java2
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java (renamed from tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java)16
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java42
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java12
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java20
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java14
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java2
-rw-r--r--tests/PackageWatchdog/Android.bp1
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java377
-rw-r--r--tests/net/Android.bp2
-rw-r--r--tools/aapt2/cmd/Optimize.cpp50
-rw-r--r--tools/aapt2/cmd/Optimize.h15
-rw-r--r--tools/aapt2/cmd/Optimize_test.cpp68
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp14
-rw-r--r--tools/aapt2/format/binary/TableFlattener.h5
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp15
-rwxr-xr-xtools/codegen/src/com/android/codegen/Main.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/SharedConstants.kt2
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt5
-rw-r--r--tools/protologtool/src/com/android/protologtool/SourceTransformer.kt36
-rw-r--r--tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt102
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java7
473 files changed, 13066 insertions, 6267 deletions
diff --git a/Android.bp b/Android.bp
index 121decbbec50..35c7b1bc6fdd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -234,6 +234,11 @@ java_defaults {
"wifi/java",
],
},
+
+ required: [
+ // TODO: remove gps_debug when the build system propagates "required" properly.
+ "gps_debug.conf",
+ ],
}
// Collection of classes that are generated from non-Java files that are not listed in
@@ -307,11 +312,6 @@ java_defaults {
static_libs: ["framework-internal-utils"],
- required: [
- // TODO: remove gps_debug when the build system propagates "required" properly.
- "gps_debug.conf",
- ],
-
dxflags: [
"--core-library",
"--multi-dex",
@@ -502,7 +502,6 @@ java_library {
sdk_version: "core_platform",
static_libs: [
"libphonenumber-platform",
- "nist-sip",
"tagsoup",
"rappor",
"libtextclassifier-java",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f311fc81fc10..6a909c0364e9 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -253,6 +253,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DynamicAndroidInstallationService)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DefaultContainerService)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CaptivePortalLogin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ext.jar)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 831be0bf213a..400d90203453 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -2000,10 +2000,10 @@ public final class QuotaController extends StateController {
private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
4 * HOUR_IN_MILLIS;
private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
- 10 * MINUTE_IN_MILLIS;
+ MINUTE_IN_MILLIS;
private static final int DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 20;
- private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = // 20/window = 120/hr = 1/session
- DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
+ private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
+ 75; // 75/window = 450/hr = 1/session
private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
(int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
@@ -2011,7 +2011,7 @@ public final class QuotaController extends StateController {
private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
(int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
- 20; // 120/hr
+ 75; // 450/hr
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
10; // 5/hr
private static final int DEFAULT_MAX_SESSION_COUNT_FREQUENT =
@@ -2165,7 +2165,7 @@ public final class QuotaController extends StateController {
mResolver = resolver;
mResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS), false, this);
- updateConstants();
+ onChange(true, null);
}
@Override
diff --git a/api/current.txt b/api/current.txt
index 26c90dff3c6b..2e099ed8e2a2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7216,18 +7216,18 @@ package android.app.assist {
method @Nullable public android.view.autofill.AutofillValue getAutofillValue();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
- method public String getClassName();
- method public CharSequence getContentDescription();
+ method @Nullable public String getClassName();
+ method @Nullable public CharSequence getContentDescription();
method public float getElevation();
- method public android.os.Bundle getExtras();
+ method @Nullable public android.os.Bundle getExtras();
method public int getHeight();
- method public String getHint();
+ method @Nullable public String getHint();
method @Nullable public String getHintIdEntry();
method @Nullable public android.view.ViewStructure.HtmlInfo getHtmlInfo();
method public int getId();
- method public String getIdEntry();
- method public String getIdPackage();
- method public String getIdType();
+ method @Nullable public String getIdEntry();
+ method @Nullable public String getIdPackage();
+ method @Nullable public String getIdType();
method public int getImportantForAutofill();
method public int getInputType();
method public int getLeft();
@@ -7237,12 +7237,12 @@ package android.app.assist {
method public int getMinTextEms();
method public int getScrollX();
method public int getScrollY();
- method public CharSequence getText();
+ method @Nullable public CharSequence getText();
method public int getTextBackgroundColor();
method public int getTextColor();
method @Nullable public String getTextIdEntry();
- method public int[] getTextLineBaselines();
- method public int[] getTextLineCharOffsets();
+ method @Nullable public int[] getTextLineBaselines();
+ method @Nullable public int[] getTextLineCharOffsets();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
method public float getTextSize();
@@ -7878,6 +7878,7 @@ package android.appwidget {
method public void deleteAppWidgetId(int);
method public void deleteHost();
method public int[] getAppWidgetIds();
+ method public void onAppWidgetRemoved(int);
method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
method protected void onProvidersChanged();
@@ -16906,9 +16907,10 @@ package android.hardware.camera2 {
method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(@NonNull android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException;
method @NonNull public abstract String getId();
method public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
- method public int setCameraAudioRestriction(int) throws android.hardware.camera2.CameraAccessException;
+ method public void setCameraAudioRestriction(int) throws android.hardware.camera2.CameraAccessException;
field public static final int AUDIO_RESTRICTION_NONE = 0; // 0x0
field public static final int AUDIO_RESTRICTION_VIBRATION = 1; // 0x1
field public static final int AUDIO_RESTRICTION_VIBRATION_SOUND = 3; // 0x3
@@ -50447,6 +50449,7 @@ package android.view {
method public final int getScrollY();
method @android.view.ViewDebug.ExportedProperty(category="drawing") @ColorInt public int getSolidColor();
method @LayoutRes public int getSourceLayoutResId();
+ method @android.view.ViewDebug.ExportedProperty(category="accessibility") @Nullable public final CharSequence getStateDescription();
method public android.animation.StateListAnimator getStateListAnimator();
method protected int getSuggestedMinimumHeight();
method protected int getSuggestedMinimumWidth();
@@ -50791,6 +50794,7 @@ package android.view {
method public void setScrollbarFadingEnabled(boolean);
method public void setSelected(boolean);
method public void setSoundEffectsEnabled(boolean);
+ method public void setStateDescription(@Nullable CharSequence);
method public void setStateListAnimator(android.animation.StateListAnimator);
method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
method public void setSystemUiVisibility(int);
@@ -52145,6 +52149,7 @@ package android.view.accessibility {
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+ field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
@@ -52262,6 +52267,7 @@ package android.view.accessibility {
method @Nullable public CharSequence getPaneTitle();
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
+ method @Nullable public CharSequence getStateDescription();
method public CharSequence getText();
method public int getTextSelectionEnd();
method public int getTextSelectionStart();
@@ -52353,6 +52359,7 @@ package android.view.accessibility {
method public void setShowingHintText(boolean);
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
+ method public void setStateDescription(@Nullable CharSequence);
method public void setText(CharSequence);
method public void setTextEntryKey(boolean);
method public void setTextSelection(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2843916c25b7..4c48238d655e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -140,6 +140,7 @@ package android {
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
@@ -5515,7 +5516,7 @@ package android.os {
public class UpdateEngine {
ctor public UpdateEngine();
method public void applyPayload(String, long, long, String[]);
- method public void applyPayload(java.io.FileDescriptor, long, long, String[]);
+ method public void applyPayload(@NonNull java.io.FileDescriptor, long, long, @NonNull String[]);
method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
@@ -5707,6 +5708,7 @@ package android.permission {
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @BinderThread public void onUpdateUserSensitive();
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
@@ -7234,6 +7236,13 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
}
+ public class CallerInfo {
+ method @Nullable public android.net.Uri getContactDisplayPhotoUri();
+ method public long getContactId();
+ method @Nullable public String getName();
+ method @Nullable public String getPhoneNumber();
+ }
+
public class CarrierConfigManager {
method @NonNull public static android.os.PersistableBundle getDefaultConfig();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle);
@@ -7726,6 +7735,24 @@ package android.telephony {
field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+ method public int describeContents();
+ method public int getIdleTimeMillis();
+ method public int getReceiveTimeMillis();
+ method public int getSleepTimeMillis();
+ method public long getTimestamp();
+ method @NonNull public java.util.List<android.telephony.ModemActivityInfo.TransmitPower> getTransmitPowerInfo();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVELS = 5; // 0x5
+ }
+
+ public class ModemActivityInfo.TransmitPower {
+ method @NonNull public android.util.Range<java.lang.Integer> getPowerRangeInDbm();
+ method public int getTimeInMillis();
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method public int describeContents();
method public int getAccessNetworkTechnology();
@@ -7827,6 +7854,8 @@ package android.telephony {
field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 268435456; // 0x10000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 536870912; // 0x20000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
@@ -9487,7 +9516,8 @@ package android.telephony.ims.stub {
method public void onReady();
method public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException;
+ method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
method public void sendSms(int, int, String, String, boolean, byte[]);
field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index f92502370566..8be95e4659b4 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -22,7 +22,6 @@ cc_binary {
"libcutils",
"libdl",
"libhidlbase",
- "libhwbinder",
"liblog",
"libnativeloader",
"libutils",
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index c9a4b3ba6368..43058d538552 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -51,11 +51,6 @@ cc_defaults {
srcs: [
":statsd_aidl",
"src/active_config_list.proto",
- "src/statsd_config.proto",
- "src/uid_data.proto",
- "src/FieldValue.cpp",
- "src/hash.cpp",
- "src/stats_log_util.cpp",
"src/anomaly/AlarmMonitor.cpp",
"src/anomaly/AlarmTracker.cpp",
"src/anomaly/AnomalyTracker.cpp",
@@ -63,51 +58,56 @@ cc_defaults {
"src/anomaly/subscriber_util.cpp",
"src/condition/CombinationConditionTracker.cpp",
"src/condition/condition_util.cpp",
- "src/condition/SimpleConditionTracker.cpp",
"src/condition/ConditionWizard.cpp",
+ "src/condition/SimpleConditionTracker.cpp",
"src/condition/StateConditionTracker.cpp",
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
"src/external/GpuStatsPuller.cpp",
"src/external/Perfetto.cpp",
- "src/external/StatsPuller.cpp",
+ "src/external/PowerStatsPuller.cpp",
+ "src/external/puller_util.cpp",
+ "src/external/ResourceHealthManagerPuller.cpp",
"src/external/StatsCallbackPuller.cpp",
"src/external/StatsCompanionServicePuller.cpp",
+ "src/external/StatsPuller.cpp",
+ "src/external/StatsPullerManager.cpp",
"src/external/SubsystemSleepStatePuller.cpp",
- "src/external/PowerStatsPuller.cpp",
- "src/external/ResourceHealthManagerPuller.cpp",
"src/external/TrainInfoPuller.cpp",
- "src/external/StatsPullerManager.cpp",
- "src/external/puller_util.cpp",
+ "src/FieldValue.cpp",
+ "src/guardrail/StatsdStats.cpp",
+ "src/hash.cpp",
+ "src/HashableDimensionKey.cpp",
"src/logd/LogEvent.cpp",
"src/logd/LogEventQueue.cpp",
"src/matchers/CombinationLogMatchingTracker.cpp",
"src/matchers/EventMatcherWizard.cpp",
"src/matchers/matcher_util.cpp",
"src/matchers/SimpleLogMatchingTracker.cpp",
- "src/metrics/MetricProducer.cpp",
- "src/metrics/EventMetricProducer.cpp",
"src/metrics/CountMetricProducer.cpp",
- "src/metrics/DurationMetricProducer.cpp",
- "src/metrics/duration_helper/OringDurationTracker.cpp",
"src/metrics/duration_helper/MaxDurationTracker.cpp",
- "src/metrics/ValueMetricProducer.cpp",
+ "src/metrics/duration_helper/OringDurationTracker.cpp",
+ "src/metrics/DurationMetricProducer.cpp",
+ "src/metrics/EventMetricProducer.cpp",
"src/metrics/GaugeMetricProducer.cpp",
- "src/metrics/MetricsManager.cpp",
+ "src/metrics/MetricProducer.cpp",
"src/metrics/metrics_manager_util.cpp",
+ "src/metrics/MetricsManager.cpp",
+ "src/metrics/ValueMetricProducer.cpp",
"src/packages/UidMap.cpp",
- "src/storage/StorageManager.cpp",
+ "src/shell/shell_config.proto",
+ "src/shell/ShellSubscriber.cpp",
+ "src/socket/StatsSocketListener.cpp",
+ "src/stats_log_util.cpp",
+ "src/statscompanion_util.cpp",
+ "src/statsd_config.proto",
"src/StatsLogProcessor.cpp",
"src/StatsService.cpp",
- "src/statscompanion_util.cpp",
+ "src/storage/StorageManager.cpp",
"src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp",
- "src/HashableDimensionKey.cpp",
- "src/guardrail/StatsdStats.cpp",
- "src/socket/StatsSocketListener.cpp",
- "src/shell/ShellSubscriber.cpp",
- "src/shell/shell_config.proto",
+ "src/uid_data.proto",
],
local_include_dirs: [
@@ -120,23 +120,23 @@ cc_defaults {
],
shared_libs: [
+ "android.frameworks.stats@1.0",
+ "android.hardware.health@2.0",
+ "android.hardware.power.stats@1.0",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
"libbase",
"libbinder",
+ "libcutils",
"libgraphicsenv",
+ "libhidlbase",
"libincident",
"liblog",
- "libutils",
- "libservices",
"libprotoutil",
+ "libservices",
"libstatslog",
- "libhidlbase",
- "android.frameworks.stats@1.0",
- "android.hardware.health@2.0",
- "android.hardware.power@1.0",
- "android.hardware.power@1.1",
- "android.hardware.power.stats@1.0",
"libsysutils",
- "libcutils",
+ "libutils",
],
}
@@ -210,54 +210,54 @@ cc_test {
"src/atom_field_options.proto",
"src/atoms.proto",
- "src/stats_log.proto",
"src/shell/shell_data.proto",
+ "src/stats_log.proto",
"tests/AlarmMonitor_test.cpp",
"tests/anomaly/AlarmTracker_test.cpp",
"tests/anomaly/AnomalyTracker_test.cpp",
+ "tests/condition/CombinationConditionTracker_test.cpp",
+ "tests/condition/ConditionTimer_test.cpp",
+ "tests/condition/SimpleConditionTracker_test.cpp",
+ "tests/condition/StateConditionTracker_test.cpp",
"tests/ConfigManager_test.cpp",
- "tests/external/puller_util_test.cpp",
+ "tests/e2e/Alarm_e2e_test.cpp",
+ "tests/e2e/Anomaly_count_e2e_test.cpp",
+ "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
+ "tests/e2e/Attribution_e2e_test.cpp",
+ "tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/DurationMetric_e2e_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_push_test.cpp",
+ "tests/e2e/MetricActivation_e2e_test.cpp",
+ "tests/e2e/MetricConditionLink_e2e_test.cpp",
+ "tests/e2e/PartialBucket_e2e_test.cpp",
+ "tests/e2e/ValueMetric_pull_e2e_test.cpp",
+ "tests/e2e/WakelockDuration_e2e_test.cpp",
"tests/external/GpuStatsPuller_test.cpp",
"tests/external/IncidentReportArgs_test.cpp",
+ "tests/external/puller_util_test.cpp",
"tests/external/StatsPuller_test.cpp",
+ "tests/FieldValue_test.cpp",
+ "tests/guardrail/StatsdStats_test.cpp",
"tests/indexed_priority_queue_test.cpp",
+ "tests/log_event/LogEventQueue_test.cpp",
"tests/LogEntryMatcher_test.cpp",
"tests/LogEvent_test.cpp",
- "tests/log_event/LogEventQueue_test.cpp",
- "tests/MetricsManager_test.cpp",
- "tests/StatsLogProcessor_test.cpp",
- "tests/StatsService_test.cpp",
- "tests/UidMap_test.cpp",
- "tests/FieldValue_test.cpp",
- "tests/condition/CombinationConditionTracker_test.cpp",
- "tests/condition/SimpleConditionTracker_test.cpp",
- "tests/condition/StateConditionTracker_test.cpp",
- "tests/condition/ConditionTimer_test.cpp",
- "tests/metrics/OringDurationTracker_test.cpp",
- "tests/metrics/MaxDurationTracker_test.cpp",
"tests/metrics/CountMetricProducer_test.cpp",
"tests/metrics/DurationMetricProducer_test.cpp",
"tests/metrics/EventMetricProducer_test.cpp",
- "tests/metrics/ValueMetricProducer_test.cpp",
"tests/metrics/GaugeMetricProducer_test.cpp",
- "tests/guardrail/StatsdStats_test.cpp",
+ "tests/metrics/MaxDurationTracker_test.cpp",
"tests/metrics/metrics_test_helper.cpp",
+ "tests/metrics/OringDurationTracker_test.cpp",
+ "tests/metrics/ValueMetricProducer_test.cpp",
+ "tests/MetricsManager_test.cpp",
+ "tests/shell/ShellSubscriber_test.cpp",
"tests/statsd_test_util.cpp",
+ "tests/StatsLogProcessor_test.cpp",
+ "tests/StatsService_test.cpp",
"tests/storage/StorageManager_test.cpp",
- "tests/e2e/WakelockDuration_e2e_test.cpp",
- "tests/e2e/MetricActivation_e2e_test.cpp",
- "tests/e2e/MetricConditionLink_e2e_test.cpp",
- "tests/e2e/Alarm_e2e_test.cpp",
- "tests/e2e/Attribution_e2e_test.cpp",
- "tests/e2e/GaugeMetric_e2e_push_test.cpp",
- "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
- "tests/e2e/ValueMetric_pull_e2e_test.cpp",
- "tests/e2e/Anomaly_count_e2e_test.cpp",
- "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
- "tests/e2e/ConfigTtl_e2e_test.cpp",
- "tests/e2e/PartialBucket_e2e_test.cpp",
- "tests/e2e/DurationMetric_e2e_test.cpp",
- "tests/shell/ShellSubscriber_test.cpp",
+ "tests/UidMap_test.cpp",
],
static_libs: [
@@ -287,17 +287,17 @@ cc_benchmark {
// not included in libprotobuf-cpp-lite, so compile it here.
":libprotobuf-internal-protos",
- "src/atom_field_options.proto",
- "src/atoms.proto",
- "src/stats_log.proto",
- "benchmark/main.cpp",
- "benchmark/hello_world_benchmark.cpp",
- "benchmark/log_event_benchmark.cpp",
- "benchmark/stats_write_benchmark.cpp",
+ "benchmark/duration_metric_benchmark.cpp",
"benchmark/filter_value_benchmark.cpp",
"benchmark/get_dimensions_for_condition_benchmark.cpp",
+ "benchmark/hello_world_benchmark.cpp",
+ "benchmark/log_event_benchmark.cpp",
+ "benchmark/main.cpp",
"benchmark/metric_util.cpp",
- "benchmark/duration_metric_benchmark.cpp",
+ "benchmark/stats_write_benchmark.cpp",
+ "src/atom_field_options.proto",
+ "src/atoms.proto",
+ "src/stats_log.proto",
],
proto: {
@@ -337,11 +337,11 @@ java_library {
},
srcs: [
- "src/stats_log.proto",
- "src/statsd_config.proto",
"src/atoms.proto",
"src/shell/shell_config.proto",
"src/shell/shell_data.proto",
+ "src/stats_log.proto",
+ "src/statsd_config.proto",
],
static_libs: [
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ae751ce6d7ab..b50ec8a84b40 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3968,7 +3968,7 @@ message ModemActivityInfo {
// rx time in ms at power level 5
optional uint64 controller_rx_time_millis = 9;
// product of current(mA), voltage(V) and time(ms)
- optional uint64 energy_used = 10;
+ optional uint64 energy_used = 10 [deprecated=true];
}
/**
@@ -4094,9 +4094,13 @@ message ProcessMemoryHighWaterMark {
// Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
+ // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is
+ // computed by converting kilobytes to bytes.
+ optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true];
+
// RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
// /proc/PID/status.
- optional int64 rss_high_water_mark_in_bytes = 3;
+ optional int32 rss_high_water_mark_in_kilobytes = 4;
}
/*
diff --git a/config/dirty-image-objects b/config/dirty-image-objects
index ec2568dc4ad8..2dfe0197d898 100644
--- a/config/dirty-image-objects
+++ b/config/dirty-image-objects
@@ -255,7 +255,7 @@ com.android.internal.os.SomeArgs
com.android.internal.policy.DecorView
com.android.internal.statusbar.IStatusBarService
com.android.internal.telephony.AppSmsManager
-com.android.internal.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener
+android.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener
com.android.internal.telephony.CarrierActionAgent
com.android.internal.telephony.cat.CatService
com.android.internal.telephony.cat.IconLoader
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 778a4d7bcda7..e117e689b598 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4698,7 +4698,7 @@ com.android.internal.telephony.CallManager$CallManagerHandler
com.android.internal.telephony.CallManager
com.android.internal.telephony.CallStateException
com.android.internal.telephony.CallTracker
-com.android.internal.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener
+android.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener
com.android.internal.telephony.CarrierActionAgent$1
com.android.internal.telephony.CarrierActionAgent
com.android.internal.telephony.CarrierAppUtils
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index f05edee9c42d..353f786da153 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -4,4 +4,3 @@ android.os.FileObserver
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
sun.nio.fs.UnixChannelFactory
-android.permission.PermissionManager
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 15c642571bc6..bf7d6324764f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -146,6 +146,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -945,6 +946,10 @@ public class Activity extends ContextThemeWrapper
/** @hide */
boolean mEnterAnimationComplete;
+ /** Track last dispatched multi-window and PiP mode to client, internal debug purpose **/
+ private Boolean mLastDispatchedIsInMultiWindowMode;
+ private Boolean mLastDispatchedIsInPictureInPictureMode;
+
private static native String getDlWarning();
/** Return the intent that started this activity. */
@@ -3646,6 +3651,22 @@ public class Activity extends ContextThemeWrapper
return false;
}
+ private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
+ private final WeakReference<Activity> mActivityRef;
+
+ RequestFinishCallback(WeakReference<Activity> activityRef) {
+ mActivityRef = activityRef;
+ }
+
+ @Override
+ public void requestFinish() {
+ Activity activity = mActivityRef.get();
+ if (activity != null) {
+ activity.mHandler.post(activity::finishAfterTransition);
+ }
+ }
+ }
+
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
@@ -3671,11 +3692,7 @@ public class Activity extends ContextThemeWrapper
// while at the root of the task. This call allows ActivityTaskManager
// to intercept or defer finishing.
ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
- new IRequestFinishCallback.Stub() {
- public void requestFinish() {
- mHandler.post(() -> finishAfterTransition());
- }
- });
+ new RequestFinishCallback(new WeakReference<>(this)));
} catch (RemoteException e) {
finishAfterTransition();
}
@@ -6990,6 +7007,10 @@ public class Activity extends ContextThemeWrapper
writer.print(mResumed); writer.print(" mStopped=");
writer.print(mStopped); writer.print(" mFinished=");
writer.println(mFinished);
+ writer.print(innerPrefix); writer.print("mLastDispatchedIsInMultiWindowMode=");
+ writer.print(mLastDispatchedIsInMultiWindowMode);
+ writer.print(" mLastDispatchedIsInPictureInPictureMode=");
+ writer.println(mLastDispatchedIsInPictureInPictureMode);
writer.print(innerPrefix); writer.print("mChangingConfigurations=");
writer.println(mChangingConfigurations);
writer.print(innerPrefix); writer.print("mCurrentConfig=");
@@ -8071,6 +8092,7 @@ public class Activity extends ContextThemeWrapper
if (mWindow != null) {
mWindow.onMultiWindowModeChanged();
}
+ mLastDispatchedIsInMultiWindowMode = isInMultiWindowMode;
onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
}
@@ -8083,6 +8105,7 @@ public class Activity extends ContextThemeWrapper
if (mWindow != null) {
mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode);
}
+ mLastDispatchedIsInPictureInPictureMode = isInPictureInPictureMode;
onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1f283eafde0c..9de42c3b57d5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1218,6 +1218,9 @@ public class AppOpsManager {
OP_REQUEST_INSTALL_PACKAGES,
OP_START_FOREGROUND,
OP_SMS_FINANCIAL_TRANSACTIONS,
+ OP_MANAGE_IPSEC_TUNNELS,
+ OP_GET_USAGE_STATS,
+ OP_INSTANT_APP_START_FOREGROUND
};
/**
@@ -1598,7 +1601,7 @@ public class AppOpsManager {
Manifest.permission.REQUEST_DELETE_PACKAGES,
Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
Manifest.permission.ACCEPT_HANDOVER,
- null, // no permission for OP_MANAGE_IPSEC_TUNNELS
+ Manifest.permission.MANAGE_IPSEC_TUNNELS,
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
Manifest.permission.USE_BIOMETRIC,
@@ -5845,39 +5848,42 @@ public class AppOpsManager {
ActivityThread.currentOpPackageName())) {
// This app is noting an app-op for itself. Deliver immediately.
sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code));
- } else if (binderUid != null && binderUid == uid) {
- // We are inside of a two-way binder call. Delivered to caller via
- // {@link #prefixParcelWithAppOpsIfNeeded}
- long[] appOpsNotedInThisBinderTransaction;
-
- appOpsNotedInThisBinderTransaction = sAppOpsNotedInThisBinderTransaction.get();
- if (appOpsNotedInThisBinderTransaction == null) {
- appOpsNotedInThisBinderTransaction = new long[2];
- sAppOpsNotedInThisBinderTransaction.set(appOpsNotedInThisBinderTransaction);
- }
- if (code < 64) {
- appOpsNotedInThisBinderTransaction[0] |= 1L << code;
- } else {
- appOpsNotedInThisBinderTransaction[1] |= 1L << (code - 64);
- }
+ return;
+ }
+ }
+
+ if (binderUid != null && binderUid == uid) {
+ // If this is inside of a two-way binder call: Delivered to caller via
+ // {@link #prefixParcelWithAppOpsIfNeeded}
+ long[] appOpsNotedInThisBinderTransaction;
+
+ appOpsNotedInThisBinderTransaction = sAppOpsNotedInThisBinderTransaction.get();
+ if (appOpsNotedInThisBinderTransaction == null) {
+ appOpsNotedInThisBinderTransaction = new long[2];
+ sAppOpsNotedInThisBinderTransaction.set(appOpsNotedInThisBinderTransaction);
+ }
+
+ if (code < 64) {
+ appOpsNotedInThisBinderTransaction[0] |= 1L << code;
} else {
- // We cannot deliver the note synchronous. Hence send it to the system server to
- // notify the noted process.
- if (message == null) {
- // Default message is a stack trace
- message = getFormattedStackTrace();
- }
+ appOpsNotedInThisBinderTransaction[1] |= 1L << (code - 64);
+ }
+ } else {
+ // Cannot deliver the note synchronous: Hence send it to the system server to
+ // notify the noted process.
+ if (message == null) {
+ // Default message is a stack trace
+ message = getFormattedStackTrace();
+ }
- long token = Binder.clearCallingIdentity();
- try {
- mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
- message);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code, message);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 86e768db3c52..f9e710e118ea 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1080,6 +1080,7 @@ public class AssistStructure implements Parcelable {
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
* for more information.
*/
+ @Nullable
public String getIdPackage() {
return mIdPackage;
}
@@ -1089,6 +1090,7 @@ public class AssistStructure implements Parcelable {
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
* for more information.
*/
+ @Nullable
public String getIdType() {
return mIdType;
}
@@ -1098,6 +1100,7 @@ public class AssistStructure implements Parcelable {
* identifier. See {@link android.view.ViewStructure#setId ViewStructure.setId}
* for more information.
*/
+ @Nullable
public String getIdEntry() {
return mIdEntry;
}
@@ -1391,6 +1394,7 @@ public class AssistStructure implements Parcelable {
* For example, a button will report "android.widget.Button" meaning it behaves
* like a {@link android.widget.Button}.
*/
+ @Nullable
public String getClassName() {
return mClassName;
}
@@ -1399,6 +1403,7 @@ public class AssistStructure implements Parcelable {
* Returns any content description associated with the node, which semantically describes
* its purpose for accessibility and other uses.
*/
+ @Nullable
public CharSequence getContentDescription() {
return mContentDescription;
}
@@ -1473,6 +1478,7 @@ public class AssistStructure implements Parcelable {
* Returns any text associated with the node that is displayed to the user, or null
* if there is none.
*/
+ @Nullable
public CharSequence getText() {
return mText != null ? mText.mText : null;
}
@@ -1560,6 +1566,7 @@ public class AssistStructure implements Parcelable {
* <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
+ @Nullable
public int[] getTextLineCharOffsets() {
return mText != null ? mText.mLineCharOffsets : null;
}
@@ -1573,6 +1580,7 @@ public class AssistStructure implements Parcelable {
* <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
* not for autofill purposes.
*/
+ @Nullable
public int[] getTextLineBaselines() {
return mText != null ? mText.mLineBaselines : null;
}
@@ -1592,6 +1600,7 @@ public class AssistStructure implements Parcelable {
* Return additional hint text associated with the node; this is typically used with
* a node that takes user input, describing to the user what the input means.
*/
+ @Nullable
public String getHint() {
return mText != null ? mText.mHint : null;
}
@@ -1610,6 +1619,7 @@ public class AssistStructure implements Parcelable {
/**
* Return a Bundle containing optional vendor-specific extension information.
*/
+ @Nullable
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index f003d4bea028..c20cb253a6be 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -56,6 +56,7 @@ public class AppWidgetHost {
static final int HANDLE_PROVIDERS_CHANGED = 3;
@UnsupportedAppUsage
static final int HANDLE_VIEW_DATA_CHANGED = 4;
+ static final int HANDLE_APP_WIDGET_REMOVED = 5;
final static Object sServiceLock = new Object();
@UnsupportedAppUsage
@@ -103,6 +104,14 @@ public class AppWidgetHost {
msg.sendToTarget();
}
+ public void appWidgetRemoved(int appWidgetId) {
+ Handler handler = mWeakHandler.get();
+ if (handler == null) {
+ return;
+ }
+ handler.obtainMessage(HANDLE_APP_WIDGET_REMOVED, appWidgetId, 0).sendToTarget();
+ }
+
public void providersChanged() {
Handler handler = mWeakHandler.get();
if (handler == null) {
@@ -137,6 +146,10 @@ public class AppWidgetHost {
updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
break;
}
+ case HANDLE_APP_WIDGET_REMOVED: {
+ dispatchOnAppWidgetRemoved(msg.arg1);
+ break;
+ }
case HANDLE_PROVIDER_CHANGED: {
onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj);
break;
@@ -224,6 +237,10 @@ public class AppWidgetHost {
break;
case PendingHostUpdate.TYPE_VIEW_DATA_CHANGED:
viewDataChanged(update.appWidgetId, update.viewId);
+ break;
+ case PendingHostUpdate.TYPE_APP_WIDGET_REMOVED:
+ dispatchOnAppWidgetRemoved(update.appWidgetId);
+ break;
}
}
}
@@ -426,6 +443,21 @@ public class AppWidgetHost {
}
}
+ void dispatchOnAppWidgetRemoved(int appWidgetId) {
+ synchronized (mViews) {
+ mViews.remove(appWidgetId);
+ }
+ onAppWidgetRemoved(appWidgetId);
+ }
+
+ /**
+ * Called when the app widget is removed for appWidgetId
+ * @param appWidgetId
+ */
+ public void onAppWidgetRemoved(int appWidgetId) {
+ // Does nothing
+ }
+
/**
* Called when the set of available widgets changes (ie. widget containing packages
* are added, updated or removed, or widget components are enabled or disabled.)
diff --git a/core/java/android/appwidget/PendingHostUpdate.java b/core/java/android/appwidget/PendingHostUpdate.java
index e1bf159ccd84..1f1d64494417 100644
--- a/core/java/android/appwidget/PendingHostUpdate.java
+++ b/core/java/android/appwidget/PendingHostUpdate.java
@@ -28,6 +28,7 @@ public class PendingHostUpdate implements Parcelable {
static final int TYPE_VIEWS_UPDATE = 0;
static final int TYPE_PROVIDER_CHANGED = 1;
static final int TYPE_VIEW_DATA_CHANGED = 2;
+ static final int TYPE_APP_WIDGET_REMOVED = 3;
final int appWidgetId;
final int type;
@@ -53,6 +54,13 @@ public class PendingHostUpdate implements Parcelable {
return update;
}
+ /**
+ * IAppWidgetHost appWidgetRemoved implimentaion
+ */
+ public static PendingHostUpdate appWidgetRemoved(int appWidgetId) {
+ return new PendingHostUpdate(appWidgetId, TYPE_APP_WIDGET_REMOVED);
+ }
+
private PendingHostUpdate(int appWidgetId, int type) {
this.appWidgetId = appWidgetId;
this.type = type;
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
index dfec4e102fd4..a0aa2dee9d34 100644
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -75,16 +75,4 @@ import java.io.OutputStream;
}
mSocket.write(b, offset, count);
}
-
- /**
- * Wait until the data in sending queue is emptied. A polling version
- * for flush implementation. Use it to ensure the writing data afterwards will
- * be packed in the new RFCOMM frame.
- *
- * @throws IOException if an i/o error occurs.
- * @since Android 4.2.3
- */
- public void flush() throws IOException {
- mSocket.flush();
- }
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index a6e3153d6af7..760166bfcc5d 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -515,20 +515,6 @@ public final class BluetoothSocket implements Closeable {
return mSocketIS.available();
}
- /**
- * Wait until the data in sending queue is emptied. A polling version
- * for flush implementation. Used to ensure the writing data afterwards will
- * be packed in new RFCOMM frame.
- *
- * @throws IOException if an i/o error occurs.
- */
- @UnsupportedAppUsage
- /*package*/ void flush() throws IOException {
- if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
- if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
- mSocketOS.flush();
- }
-
/*package*/ int read(byte[] b, int offset, int length) throws IOException {
int ret = 0;
if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 9d523633feb6..099dea29eaf7 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -60,9 +60,7 @@ import java.util.Set;
* multiple possible matching values (via {@link #addAction},
* {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
* {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
- * For actions, the field
- * will not be tested if no values have been given (treating it as a wildcard);
- * if no data characteristics are specified, however, then the filter will
+ * For actions, if no data characteristics are specified, then the filter will
* only match intents that contain no data.
*
* <p>The data characteristic is
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4c970da36f45..0b157fa3bb1e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4070,32 +4070,54 @@ public class PackageParser {
intentInfo, outError)) {
return false;
}
- Intent intent = new Intent();
- if (intentInfo.countActions() != 1) {
- outError[0] = "intent tags must contain exactly one action.";
+
+ Uri data = null;
+ String dataType = null;
+ String host = "";
+ final int numActions = intentInfo.countActions();
+ final int numSchemes = intentInfo.countDataSchemes();
+ final int numTypes = intentInfo.countDataTypes();
+ final int numHosts = intentInfo.getHosts().length;
+ if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
+ outError[0] = "intent tags must contain either an action or data.";
return false;
}
- intent.setAction(intentInfo.getAction(0));
- for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
- intent.addCategory(intentInfo.getCategory(i));
+ if (numActions > 1) {
+ outError[0] = "intent tag may have at most one action.";
+ return false;
}
- Uri data = null;
- String dataType = null;
- if (intentInfo.countDataTypes() > 1) {
+ if (numTypes > 1) {
outError[0] = "intent tag may have at most one data type.";
return false;
}
- if (intentInfo.countDataSchemes() > 1) {
+ if (numSchemes > 1) {
outError[0] = "intent tag may have at most one data scheme.";
return false;
}
- if (intentInfo.countDataTypes() == 1) {
- data = Uri.fromParts(intentInfo.getDataType(0), "", null);
+ if (numHosts > 1) {
+ outError[0] = "intent tag may have at most one data host.";
+ return false;
+ }
+ Intent intent = new Intent();
+ for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+ intent.addCategory(intentInfo.getCategory(i));
+ }
+ if (numHosts == 1) {
+ host = intentInfo.getHosts()[0];
}
- if (intentInfo.countDataSchemes() == 1) {
- dataType = intentInfo.getDataScheme(0);
+ if (numSchemes == 1) {
+ data = new Uri.Builder()
+ .scheme(intentInfo.getDataScheme(0))
+ .authority(host)
+ .build();
+ }
+ if (numTypes == 1) {
+ dataType = intentInfo.getDataType(0);
}
intent.setDataAndType(data, dataType);
+ if (numActions == 1) {
+ intent.setAction(intentInfo.getAction(0));
+ }
owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent);
} else if (parser.getName().equals("package")) {
final TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index d03bea2fb19e..e65d761cb77e 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -57,7 +57,7 @@ public class UserInfo implements Parcelable {
/**
* Primary user. Only one user can have this flag set. It identifies the first human user
- * on a device.
+ * on a device. This flag is not supported in headless system user mode.
*/
@UnsupportedAppUsage
public static final int FLAG_PRIMARY = 0x00000001;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 9cf54f41a64b..ac1cbd4619df 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -23,6 +23,7 @@ import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.KEYBOARD;
import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.LOCALE_LIST;
import static android.content.ConfigurationProto.MCC;
import static android.content.ConfigurationProto.MNC;
import static android.content.ConfigurationProto.NAVIGATION;
@@ -1111,7 +1112,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
protoOutputStream.write(MCC, mcc);
protoOutputStream.write(MNC, mnc);
if (mLocaleList != null) {
- mLocaleList.writeToProto(protoOutputStream, LOCALES);
+ protoOutputStream.write(LOCALE_LIST, mLocaleList.toLanguageTags());
}
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
protoOutputStream.write(COLOR_MODE, colorMode);
@@ -1283,6 +1284,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration
case (int) WINDOW_CONFIGURATION:
windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
break;
+ case (int) LOCALE_LIST:
+ try {
+ setLocales(LocaleList.forLanguageTags(protoInputStream.readString(
+ LOCALE_LIST)));
+ } catch (Exception e) {
+ Slog.e(TAG, "error parsing locale list in configuration.", e);
+ }
+ break;
}
}
} finally {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index b8ab1142e534..8e8a81d6cee6 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -2169,11 +2169,18 @@ public class Camera {
}
/**
- * Setting camera audio restriction mode.
+ * Set camera audio restriction mode.
*
* @hide
*/
- public native final int setAudioRestriction(int mode);
+ public native final void setAudioRestriction(int mode);
+
+ /**
+ * Get currently applied camera audio restriction mode.
+ *
+ * @hide
+ */
+ public native final int getAudioRestriction();
/**
* Image size (width and height dimensions).
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ed2d0c9344c6..fb1ece29a369 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -567,6 +567,9 @@ public abstract class CameraDevice implements AutoCloseable {
* match one of the supported input sizes for {@link android.graphics.ImageFormat#PRIVATE}
* format. Otherwise, creating a reprocessable capture session will fail.</p>
*
+ * <p>Starting from API level 30, recreating a reprocessable capture session will flush all the
+ * queued but not yet processed buffers from the input surface.</p>
+ *
* <p>The guaranteed stream configurations listed in
* {@link #createCaptureSession createCaptureSession} are also guaranteed to work for
* {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition,
@@ -1249,27 +1252,47 @@ public abstract class CameraDevice implements AutoCloseable {
*
* <p>The mute mode is a system-wide setting. When multiple CameraDevice objects
* are setting different modes, the system will pick a the mode that's union of
- * all modes set by CameraDevice.</p>
+ * all modes set by CameraDevice. Applications can also use
+ * {@link #getCameraAudioRestriction} to query current system-wide camera
+ * mute mode in effect.</p>
*
- * <p>The mute settings will be automatically removed when the CameraDevice is closed or
- * the application is disconnected from the camera.</p>
+ * <p>The mute settings from this CameraDevice will be automatically removed when the
+ * CameraDevice is closed or the application is disconnected from the camera.</p>
*
* @param mode An enumeration selecting the audio restriction mode for this camera device.
*
- * @return The system-wide mute mode setting resulting from this call
- *
* @throws IllegalArgumentException if the mode is not supported
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #getCameraAudioRestriction
*/
- public @CAMERA_AUDIO_RESTRICTION int setCameraAudioRestriction(
+ public void setCameraAudioRestriction(
@CAMERA_AUDIO_RESTRICTION int mode) throws CameraAccessException {
throw new UnsupportedOperationException("Subclasses must override this method");
}
/**
+ * Get currently applied global camera audio restriction mode.
+ *
+ * <p>Application can use this method to retrieve the system-wide camera audio restriction
+ * settings described in {@link #setCameraAudioRestriction}.</p>
+ *
+ * @return The current system-wide mute mode setting in effect
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #setCameraAudioRestriction
+ */
+ public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* To be inherited by android.hardware.camera2.* code only.
* @hide
*/
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3f56dfa6d194..43c197aa737d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -880,11 +880,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @throws IllegalArgumentException if the physical camera id is not valid
*
* @param key The metadata field to write.
- * @param value The value to set the field to, which must be of a matching
+ * @param value The value to set the field to, which must be of a matching type to the key.
* @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
* via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
* @return The builder object.
- * type to the key.
*/
public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value,
@NonNull String physicalCameraId) {
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1f385dde9563..bcbc33780ec4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1026,34 +1026,35 @@ public class CameraDeviceImpl extends CameraDevice
// callback is valid
executor = checkExecutor(executor, callback);
- // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
- // the surface isn't a physical stream surface for reprocessing request
- for (CaptureRequest request : requestList) {
- if (request.getTargets().isEmpty()) {
- throw new IllegalArgumentException(
- "Each request must have at least one Surface target");
- }
+ synchronized(mInterfaceLock) {
+ checkIfCameraClosedOrInError();
- for (Surface surface : request.getTargets()) {
- if (surface == null) {
- throw new IllegalArgumentException("Null Surface targets are not allowed");
+ // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
+ // the surface isn't a physical stream surface for reprocessing request
+ for (CaptureRequest request : requestList) {
+ if (request.getTargets().isEmpty()) {
+ throw new IllegalArgumentException(
+ "Each request must have at least one Surface target");
}
- for (int i = 0; i < mConfiguredOutputs.size(); i++) {
- OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
- if (configuration.isForPhysicalCamera()
- && configuration.getSurfaces().contains(surface)) {
- if (request.isReprocess()) {
- throw new IllegalArgumentException(
- "Reprocess request on physical stream is not allowed");
+ for (Surface surface : request.getTargets()) {
+ if (surface == null) {
+ throw new IllegalArgumentException("Null Surface targets are not allowed");
+ }
+
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
+ if (configuration.isForPhysicalCamera()
+ && configuration.getSurfaces().contains(surface)) {
+ if (request.isReprocess()) {
+ throw new IllegalArgumentException(
+ "Reprocess request on physical stream is not allowed");
+ }
}
}
}
}
- }
- synchronized(mInterfaceLock) {
- checkIfCameraClosedOrInError();
if (repeating) {
stopRepeating();
}
@@ -2349,14 +2350,21 @@ public class CameraDeviceImpl extends CameraDevice
if (errorCode == ERROR_CAMERA_BUFFER) {
// Because 1 stream id could map to multiple surfaces, we need to specify both
// streamId and surfaceId.
- List<Surface> surfaces =
- mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurfaces();
- for (Surface surface : surfaces) {
+ OutputConfiguration config = mConfiguredOutputs.get(
+ resultExtras.getErrorStreamId());
+ if (config == null) {
+ Log.v(TAG, String.format(
+ "Stream %d has been removed. Skipping buffer lost callback",
+ resultExtras.getErrorStreamId()));
+ return;
+ }
+ for (Surface surface : config.getSurfaces()) {
if (!request.containsTarget(surface)) {
continue;
}
if (DEBUG) {
- Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
+ Log.v(TAG, String.format(
+ "Lost output buffer reported for frame %d, target %s",
frameNumber, surface));
}
failureDispatch = new Runnable() {
@@ -2570,11 +2578,19 @@ public class CameraDeviceImpl extends CameraDevice
}
@Override
- public @CAMERA_AUDIO_RESTRICTION int setCameraAudioRestriction(
+ public void setCameraAudioRestriction(
@CAMERA_AUDIO_RESTRICTION int mode) throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
- return mRemoteDevice.setCameraAudioRestriction(mode);
+ mRemoteDevice.setCameraAudioRestriction(mode);
+ }
+ }
+
+ @Override
+ public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+ return mRemoteDevice.getGlobalAudioRestriction();
}
}
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ff2819b25c00..3660f29f68d1 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -258,9 +258,18 @@ public class ICameraDeviceUserWrapper {
}
}
- public int setCameraAudioRestriction(int mode) throws CameraAccessException {
+ public void setCameraAudioRestriction(int mode) throws CameraAccessException {
try {
- return mRemoteDevice.setCameraAudioRestriction(mode);
+ mRemoteDevice.setCameraAudioRestriction(mode);
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
+
+ public int getGlobalAudioRestriction() throws CameraAccessException {
+ try {
+ return mRemoteDevice.getGlobalAudioRestriction();
} catch (Throwable t) {
CameraManager.throwAsPublicException(t);
throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 0fa17c1fb574..5d1435ab4c8d 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -767,14 +767,25 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
- public int setCameraAudioRestriction(int mode) {
+ public void setCameraAudioRestriction(int mode) {
if (mLegacyDevice.isClosed()) {
String err = "Cannot set camera audio restriction, device has been closed.";
Log.e(TAG, err);
throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
}
- return mLegacyDevice.setAudioRestriction(mode);
+ mLegacyDevice.setAudioRestriction(mode);
+ }
+
+ @Override
+ public int getGlobalAudioRestriction() {
+ if (mLegacyDevice.isClosed()) {
+ String err = "Cannot set camera audio restriction, device has been closed.";
+ Log.e(TAG, err);
+ throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
+ }
+
+ return mLegacyDevice.getAudioRestriction();
}
@Override
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index c0d8baab0ad1..fbc9ac3229c3 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -550,8 +550,12 @@ public class LegacyCameraDevice implements AutoCloseable {
return lastFrame;
}
- public int setAudioRestriction(int mode) {
- return mRequestThreadManager.setAudioRestriction(mode);
+ public void setAudioRestriction(int mode) {
+ mRequestThreadManager.setAudioRestriction(mode);
+ }
+
+ public int getAudioRestriction() {
+ return mRequestThreadManager.getAudioRestriction();
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a514ee139d9c..32411fbdda2f 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -1105,9 +1105,16 @@ public class RequestThreadManager {
condition.block();
}
- public int setAudioRestriction(int mode) {
+ public void setAudioRestriction(int mode) {
if (mCamera != null) {
- return mCamera.setAudioRestriction(mode);
+ mCamera.setAudioRestriction(mode);
+ }
+ throw new IllegalStateException("Camera has been released!");
+ }
+
+ public int getAudioRestriction() {
+ if (mCamera != null) {
+ return mCamera.getAudioRestriction();
}
throw new IllegalStateException("Camera has been released!");
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 972ae2a06e5f..2295d2b7d546 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -734,6 +734,28 @@ public final class HdmiControlManager {
mHotplugEventListeners = new ArrayMap<>();
/**
+ * Listener used to get HDMI Control (CEC) status (enabled/disabled) and the connected display
+ * status.
+ * @hide
+ */
+ public interface HdmiControlStatusChangeListener {
+ /**
+ * Called when HDMI Control (CEC) is enabled/disabled.
+ *
+ * @param isCecEnabled status of HDMI Control
+ * {@link android.provider.Settings.Global#HDMI_CONTROL_ENABLED}: {@code true} if enabled.
+ * @param isCecAvailable status of CEC support of the connected display (the TV).
+ * {@code true} if supported.
+ *
+ * Note: Value of isCecAvailable is only valid when isCecEnabled is true.
+ **/
+ void onStatusChange(boolean isCecEnabled, boolean isCecAvailable);
+ }
+
+ private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener>
+ mHdmiControlStatusChangeListeners = new ArrayMap<>();
+
+ /**
* Listener used to get vendor-specific commands.
*/
public interface VendorCommandListener {
@@ -826,4 +848,73 @@ public final class HdmiControlManager {
}
};
}
+
+ /**
+ * Adds a listener to get informed of {@link HdmiControlStatusChange}.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}.
+ *
+ * @param listener {@link HdmiControlStatusChangeListener} instance
+ * @see HdmiControlManager#removeHdmiControlStatusChangeListener(
+ * HdmiControlStatusChangeListener)
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ if (mHdmiControlStatusChangeListeners.containsKey(listener)) {
+ Log.e(TAG, "listener is already registered");
+ return;
+ }
+ IHdmiControlStatusChangeListener wrappedListener =
+ getHdmiControlStatusChangeListenerWrapper(listener);
+ mHdmiControlStatusChangeListeners.put(listener, wrappedListener);
+ try {
+ mService.addHdmiControlStatusChangeListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a listener to stop getting informed of {@link HdmiControlStatusChange}.
+ *
+ * @param listener {@link HdmiControlStatusChangeListener} instance to be removed
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ IHdmiControlStatusChangeListener wrappedListener =
+ mHdmiControlStatusChangeListeners.remove(listener);
+ if (wrappedListener == null) {
+ Log.e(TAG, "tried to remove not-registered listener");
+ return;
+ }
+ try {
+ mService.removeHdmiControlStatusChangeListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IHdmiControlStatusChangeListener getHdmiControlStatusChangeListenerWrapper(
+ final HdmiControlStatusChangeListener listener) {
+ return new IHdmiControlStatusChangeListener.Stub() {
+ @Override
+ public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
+ listener.onStatusChange(isCecEnabled, isCecAvailable);
+ }
+ };
+ }
+
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 95eaf75d6510..a8fed2b03cfc 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -19,6 +19,7 @@ package android.hardware.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
@@ -41,6 +42,8 @@ interface IHdmiControlService {
HdmiDeviceInfo getActiveSource();
void oneTouchPlay(IHdmiControlCallback callback);
void queryDisplayStatus(IHdmiControlCallback callback);
+ void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
+ void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
void addDeviceEventListener(IHdmiDeviceEventListener listener);
diff --git a/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl
new file mode 100644
index 000000000000..1407821b413c
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiControlStatusChangeListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+
+/**
+ * Callback interface definition for HDMI client to get informed of
+ * the CEC availability change event.
+ *
+ * @hide
+ */
+oneway interface IHdmiControlStatusChangeListener {
+
+ /**
+ * Called when HDMI Control (CEC) is enabled/disabled.
+ *
+ * @param isCecEnabled status of HDMI Control
+ * {@link android.provider.Settings.Global#HDMI_CONTROL_ENABLED}: {@code true} if enabled.
+ * @param isCecAvailable status of CEC support of the connected display (the TV).
+ * {@code true} if supported.
+ *
+ * Note: Value of isCecAvailable is only valid when isCecEnabled is true.
+ **/
+ void onStatusChange(boolean isCecEnabled, boolean isCecAvailable);
+}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index 160376bf6cba..4d71eebf69c7 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -58,8 +58,8 @@ public class KeyphraseEnrollmentInfo {
*/
private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment";
/**
- * Activity Action: Show activity for managing the keyphrases for hotword detection.
- * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase
+ * Intent Action: for managing the keyphrases for hotword detection.
+ * This needs to be defined by a service that supports enrolling users for hotword/keyphrase
* detection.
*/
public static final String ACTION_MANAGE_VOICE_KEYPHRASES =
@@ -101,7 +101,7 @@ public class KeyphraseEnrollmentInfo {
// Find the apps that supports enrollment for hotword keyhphrases,
// Pick a privileged app and obtain the information about the supported keyphrases
// from its metadata.
- List<ResolveInfo> ris = pm.queryIntentActivities(
+ List<ResolveInfo> ris = pm.queryIntentServices(
new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
if (ris == null || ris.isEmpty()) {
// No application capable of enrolling for voice keyphrases is present.
@@ -116,11 +116,11 @@ public class KeyphraseEnrollmentInfo {
for (ResolveInfo ri : ris) {
try {
ApplicationInfo ai = pm.getApplicationInfo(
- ri.activityInfo.packageName, PackageManager.GET_META_DATA);
+ ri.serviceInfo.packageName, PackageManager.GET_META_DATA);
if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
// The application isn't privileged (/system/priv-app).
// The enrollment application needs to be a privileged system app.
- Slog.w(TAG, ai.packageName + "is not a privileged system app");
+ Slog.w(TAG, ai.packageName + " is not a privileged system app");
continue;
}
if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) {
@@ -130,6 +130,8 @@ public class KeyphraseEnrollmentInfo {
continue;
}
+ Slog.i(TAG, ai.packageName + " added to keyphrase");
+
KeyphraseMetadata metadata =
getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors);
if (metadata != null) {
@@ -137,7 +139,7 @@ public class KeyphraseEnrollmentInfo {
}
} catch (PackageManager.NameNotFoundException e) {
String error = "error parsing voice enrollment meta-data for "
- + ri.activityInfo.packageName;
+ + ri.serviceInfo.packageName;
parseErrors.add(error + ": " + e);
Slog.w(TAG, error, e);
}
@@ -290,7 +292,7 @@ public class KeyphraseEnrollmentInfo {
}
/**
- * Returns an intent to launch an activity that manages the given keyphrase
+ * Returns an intent to launch an service that manages the given keyphrase
* for the locale.
*
* @param action The enrollment related action that this intent is supposed to perform.
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 3cd9eb8c7e82..c8dbd16005ac 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -55,6 +55,18 @@ interface IUsbManager
*/
void setAccessoryPackage(in UsbAccessory accessory, String packageName, int userId);
+ /* Adds packages to the set of "denied and don't ask again" launch preferences for a device */
+ void addDevicePackagesToPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user);
+
+ /* Adds packages to the set of "denied and don't ask again" launch preferences for an accessory */
+ void addAccessoryPackagesToPreferenceDenied(in UsbAccessory accessory, in String[] packageNames, in UserHandle user);
+
+ /* Removes packages from the set of "denied and don't ask again" launch preferences for a device */
+ void removeDevicePackagesFromPreferenceDenied(in UsbDevice device, in String[] packageNames, in UserHandle user);
+
+ /* Removes packages from the set of "denied and don't ask again" launch preferences for an accessory */
+ void removeAccessoryPackagesFromPreferenceDenied(in UsbAccessory device, in String[] packageNames, in UserHandle user);
+
/* Sets the persistent permission granted state for USB device
*/
void setDevicePersistentPermission(in UsbDevice device, int uid, in UserHandle user, boolean shouldBeGranted);
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index fe7632c25445..b066a15106af 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -157,40 +157,6 @@ class LocalSocketImpl
write_native(b, myFd);
}
}
-
- /**
- * Wait until the data in sending queue is emptied. A polling version
- * for flush implementation.
- * @throws IOException
- * if an i/o error occurs.
- */
- @Override
- public void flush() throws IOException {
- FileDescriptor myFd = fd;
- if (myFd == null) throw new IOException("socket closed");
-
- // Loop until the output buffer is empty.
- Int32Ref pending = new Int32Ref(0);
- while (true) {
- try {
- // See linux/net/unix/af_unix.c
- Os.ioctlInt(myFd, OsConstants.TIOCOUTQ, pending);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
-
- if (pending.value <= 0) {
- // The output buffer is empty.
- break;
- }
-
- try {
- Thread.sleep(10);
- } catch (InterruptedException ie) {
- break;
- }
- }
- }
}
private native int read_native(FileDescriptor fd) throws IOException;
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 7782753e4abc..0de09efad8ea 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -21,9 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
-import android.content.LocaleProto;
import android.icu.util.ULocale;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -143,26 +141,6 @@ public final class LocaleList implements Parcelable {
}
/**
- * Helper to write LocaleList to a protocol buffer output stream. Assumes the parent
- * protobuf has declared the locale as repeated.
- *
- * @param protoOutputStream Stream to write the locale to.
- * @param fieldId Field Id of the Locale as defined in the parent message.
- * @hide
- */
- public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
- for (int i = 0; i < mList.length; i++) {
- final Locale locale = mList[i];
- final long token = protoOutputStream.start(fieldId);
- protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
- protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
- protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
- protoOutputStream.write(LocaleProto.SCRIPT, locale.getScript());
- protoOutputStream.end(token);
- }
- }
-
- /**
* Retrieves a String representation of the language tags in this list.
*/
@NonNull
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 29af17afc872..dd5e20e39904 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.IUpdateEngine;
import android.os.IUpdateEngineCallback;
@@ -320,8 +321,8 @@ public class UpdateEngine {
* <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and
* {@code headerKeyValuePairs} parameters.
*/
- public void applyPayload(FileDescriptor fd, long offset, long size,
- String[] headerKeyValuePairs) {
+ public void applyPayload(@NonNull FileDescriptor fd, long offset, long size,
+ @NonNull String[] headerKeyValuePairs) {
try {
mUpdateEngine.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
} catch (RemoteException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index baf748f4a20d..af574da084f0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1404,7 +1404,7 @@ public class UserManager {
/**
* Used to check if this process is running under the primary user. The primary user
- * is the first human user on a device.
+ * is the first human user on a device. This is not supported in headless system user mode.
*
* @return whether this process is running under the primary user.
* @hide
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 23c54f450a67..1c78b081120a 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -72,6 +72,8 @@ public class VintfObject {
* ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
* "android.hardware.camera.device@3.2"]. There are no duplicates.
*
+ * For AIDL HALs, the version is stripped away
+ * (e.g. "android.hardware.light").
* @hide
*/
@TestApi
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index ec0fe926b599..654b0f7d30aa 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -42,4 +42,5 @@ oneway interface IPermissionController {
void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
String permission, int grantState, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
+ void updateUserSensitive(in AndroidFuture callback);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index d012ac79297e..923d9f85cc79 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -599,4 +599,16 @@ public final class PermissionControllerManager {
}
}, executor);
}
+
+ /**
+ * @see PermissionControllerService#onUpdateUserSensitive()
+ * @hide
+ */
+ public void updateUserSensitive() {
+ mRemoteService.postAsync(service -> {
+ AndroidFuture<Void> future = new AndroidFuture<>();
+ service.updateUserSensitive(future);
+ return future;
+ });
+ }
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 6dd9b97805e2..7363d7783903 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -190,6 +190,18 @@ public abstract class PermissionControllerService extends Service {
public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback);
/**
+ * Called by system to update the
+ * {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions.
+ * <p>
+ * This is typically when creating a new user or upgrading either system or
+ * permission controller package.
+ */
+ @BinderThread
+ public void onUpdateUserSensitive() {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
+ /**
* Set the runtime permission state from a device admin.
*
* @param callerPackageName The package name of the admin requesting the change
@@ -380,6 +392,14 @@ public abstract class PermissionControllerService extends Service {
onGrantOrUpgradeDefaultRuntimePermissions(() -> callback.complete(true));
}
+
+ @Override
+ public void updateUserSensitive(AndroidFuture callback) {
+ Preconditions.checkNotNull(callback, "callback cannot be null");
+
+ onUpdateUserSensitive();
+ callback.complete(null);
+ }
};
}
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0827fd6e725e..0973a6412384 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -42,7 +42,7 @@ import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.telephony.CallerInfo;
+import android.telephony.CallerInfo;
import com.android.internal.telephony.PhoneConstants;
import java.util.List;
@@ -728,10 +728,11 @@ public class CallLog {
String accountAddress = getLogAccountAddress(context, accountHandle);
int numberPresentation = getLogNumberPresentation(number, presentation);
+ String name = (ci != null) ? ci.getName() : "";
if (numberPresentation != PRESENTATION_ALLOWED) {
number = "";
if (ci != null) {
- ci.name = "";
+ name = "";
}
}
@@ -760,9 +761,7 @@ public class CallLog {
values.put(PHONE_ACCOUNT_ID, accountId);
values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
values.put(NEW, Integer.valueOf(1));
- if ((ci != null) && (ci.name != null)) {
- values.put(CACHED_NAME, ci.name);
- }
+ values.put(CACHED_NAME, name);
values.put(ADD_FOR_ALL_USERS, addForAllUsers ? 1 : 0);
if (callType == MISSED_TYPE) {
@@ -773,7 +772,7 @@ public class CallLog {
values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName));
values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
- if ((ci != null) && (ci.contactIdOrZero > 0)) {
+ if ((ci != null) && (ci.getContactId() > 0)) {
// Update usage information for the number associated with the contact ID.
// We need to use both the number and the ID for obtaining a data ID since other
// contacts may have the same number.
@@ -787,17 +786,18 @@ public class CallLog {
cursor = resolver.query(Phone.CONTENT_URI,
new String[] { Phone._ID },
Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
- new String[] { String.valueOf(ci.contactIdOrZero),
+ new String[] { String.valueOf(ci.getContactId()),
normalizedPhoneNumber},
null);
} else {
- final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
+ final String phoneNumber = ci.getPhoneNumber() != null
+ ? ci.getPhoneNumber() : number;
cursor = resolver.query(
Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
Uri.encode(phoneNumber)),
new String[] { Phone._ID },
Phone.CONTACT_ID + " =?",
- new String[] { String.valueOf(ci.contactIdOrZero) },
+ new String[] { String.valueOf(ci.getContactId()) },
null);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7115da2bb531..8b20f0bec7be 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2103,10 +2103,6 @@ public final class Settings {
private static final String TAG = "Settings";
private static final boolean LOCAL_LOGV = false;
- // Lock ensures that when enabling/disabling the master location switch, we don't end up
- // with a partial enable/disable state in multi-threaded situations.
- private static final Object mLocationSettingsLock = new Object();
-
// Used in system server calling uid workaround in call()
private static boolean sInSystemServer = false;
private static final Object sInSystemServerLock = new Object();
@@ -8811,13 +8807,6 @@ public final class Settings {
"location_ignore_settings_package_whitelist";
/**
- * Whether to disable location status callbacks in preparation for deprecation.
- * @hide
- */
- public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
- "location_disable_status_callbacks";
-
- /**
* Maximum staleness allowed for last location when returned to clients with only foreground
* location permissions.
* @hide
@@ -12927,8 +12916,7 @@ public final class Settings {
}
/**
- * Subscription to be used for voice call on a multi sim device. The supported values
- * are 0 = SUB1, 1 = SUB2 and etc.
+ * Subscription Id to be used for voice call on a multi sim device.
* @hide
*/
public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call";
@@ -12942,15 +12930,13 @@ public final class Settings {
public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt";
/**
- * Subscription to be used for data call on a multi sim device. The supported values
- * are 0 = SUB1, 1 = SUB2 and etc.
+ * Subscription Id to be used for data call on a multi sim device.
* @hide
*/
public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call";
/**
- * Subscription to be used for SMS on a multi sim device. The supported values
- * are 0 = SUB1, 1 = SUB2 and etc.
+ * Subscription Id to be used for SMS on a multi sim device.
* @hide
*/
public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms";
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bd953cad2b75..cf56eae4a052 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -20,7 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
-import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -32,7 +32,6 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.media.AudioFormat;
import android.os.AsyncTask;
import android.os.Handler;
@@ -447,7 +446,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is un-enrolled,
* i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
* otherwise {@link #createReEnrollIntent()} should be preferred.
@@ -469,7 +468,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the un-enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is already enrolled,
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
@@ -490,7 +489,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the re-enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is already enrolled,
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 07cecd38620d..74cff205dabc 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -67,20 +67,19 @@ public class TimeUtils {
* found.
*/
private static android.icu.util.TimeZone getIcuTimeZone(
- int offset, boolean dst, long when, String country) {
- if (country == null) {
+ int offsetMillis, boolean isDst, long whenMillis, String countryIso) {
+ if (countryIso == null) {
return null;
}
android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
CountryTimeZones countryTimeZones =
- TimeZoneFinder.getInstance().lookupCountryTimeZones(country);
+ TimeZoneFinder.getInstance().lookupCountryTimeZones(countryIso);
if (countryTimeZones == null) {
return null;
}
-
- CountryTimeZones.OffsetResult offsetResult =
- countryTimeZones.lookupByOffsetWithBias(offset, dst, when, bias);
+ CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias(
+ offsetMillis, isDst, null /* dstOffsetMillis */, whenMillis, bias);
return offsetResult != null ? offsetResult.mTimeZone : null;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bb9867a298c3..1c3294858db8 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -640,4 +640,14 @@ interface IWindowManager
* native InputManager before proceeding with tests.
*/
void syncInputTransactions();
+
+ /**
+ * Returns whether SurfaceFlinger layer tracing is enabled.
+ */
+ boolean isLayerTracing();
+
+ /**
+ * Enables/disables SurfaceFlinger layer tracing.
+ */
+ void setLayerTracing(boolean enabled);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index f73f28a943c3..6ce5ac4197ea 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -31,7 +31,7 @@ import android.view.WindowManager;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.Transaction;
+import android.view.SurfaceControl.Transaction;
import java.util.List;
@@ -156,7 +156,7 @@ interface IWindowSession {
* is null if there is no sync required.
*/
@UnsupportedAppUsage
- void finishDrawing(IWindow window, in Transaction postDrawTransaction);
+ void finishDrawing(IWindow window, in SurfaceControl.Transaction postDrawTransaction);
@UnsupportedAppUsage
void setInTouchMode(boolean showFocus);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 50ef91f90997..341c2147c64a 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -26,7 +26,6 @@ import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
-import android.os.UidProto.Sync;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -40,7 +39,6 @@ import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -238,7 +236,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
state.getSource(source.getType()).setFrame(mTmpFrame);
- surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f, inset != 0));
+
+ // If the system is controlling the insets source, the leash can be null.
+ if (leash != null) {
+ surfaceParams.add(new SurfaceParams(leash, 1f /* alpha */, mTmpMatrix,
+ null /* windowCrop */, 0 /* layer */, 0f /* cornerRadius*/, inset != 0));
+ }
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 9edccb3fb221..08d45a746dc4 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -167,7 +167,7 @@ public class InsetsSourceConsumer {
}
private void applyHiddenToControl() {
- if (mSourceControl == null) {
+ if (mSourceControl == null || mSourceControl.getLeash() == null) {
return;
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 4940981748a8..4919074ec252 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,10 +29,10 @@ import android.view.InsetsState.InternalInsetType;
public class InsetsSourceControl implements Parcelable {
private final @InternalInsetType int mType;
- private final SurfaceControl mLeash;
+ private final @Nullable SurfaceControl mLeash;
private final Point mSurfacePosition;
- public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash,
+ public InsetsSourceControl(@InternalInsetType int type, @Nullable SurfaceControl leash,
Point surfacePosition) {
mType = type;
mLeash = leash;
@@ -42,7 +43,13 @@ public class InsetsSourceControl implements Parcelable {
return mType;
}
- public SurfaceControl getLeash() {
+ /**
+ * Gets the leash for controlling insets source. If the system is controlling the insets source,
+ * for example, transient bars, the client will receive fake controls without leash in it.
+ *
+ * @return the leash.
+ */
+ public @Nullable SurfaceControl getLeash() {
return mLeash;
}
diff --git a/core/java/android/view/SurfaceControl.aidl b/core/java/android/view/SurfaceControl.aidl
index 744ead2be643..6c51ae934ba3 100644
--- a/core/java/android/view/SurfaceControl.aidl
+++ b/core/java/android/view/SurfaceControl.aidl
@@ -17,3 +17,4 @@
package android.view;
parcelable SurfaceControl;
+parcelable SurfaceControl.Transaction;
diff --git a/core/java/android/view/Transaction.aidl b/core/java/android/view/Transaction.aidl
deleted file mode 100644
index 8d24b437e044..000000000000
--- a/core/java/android/view/Transaction.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.SurfaceControl;
-
-parcelable Transaction;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7ee3973a40aa..cfb6a79a674c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4356,6 +4356,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private Insets mLayoutInsets;
/**
+ * Briefly describes the state of the view and is primarily used for accessibility support.
+ */
+ private CharSequence mStateDescription;
+
+ /**
* Briefly describes the view and is primarily used for accessibility support.
*/
private CharSequence mContentDescription;
@@ -9925,6 +9930,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
info.setImportantForAccessibility(isImportantForAccessibility());
info.setPackageName(mContext.getPackageName());
info.setClassName(getAccessibilityClassName());
+ info.setStateDescription(getStateDescription());
info.setContentDescription(getContentDescription());
info.setEnabled(isEnabled());
@@ -10297,6 +10303,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns the {@link View}'s state description.
+ * <p>
+ * <strong>Note:</strong> Do not override this method, as it will have no
+ * effect on the state description presented to accessibility services.
+ * You must call {@link #setStateDescription(CharSequence)} to modify the
+ * state description.
+ *
+ * @return the state description
+ * @see #setStateDescription(CharSequence)
+ */
+ @ViewDebug.ExportedProperty(category = "accessibility")
+ public final @Nullable CharSequence getStateDescription() {
+ return mStateDescription;
+ }
+
+ /**
* Returns the {@link View}'s content description.
* <p>
* <strong>Note:</strong> Do not override this method, as it will have no
@@ -10315,6 +10337,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Sets the {@link View}'s state description.
+ * <p>
+ * A state description briefly describes the states of the view and is primarily used
+ * for accessibility support to determine how the states of a view should be presented to
+ * the user. It is a supplement to the boolean states (for example, checked/unchecked) and
+ * it is used for customized state description (for example, "wifi, connected, three bars").
+ * State description changes frequently while content description should change less often.
+ *
+ * @param stateDescription The state description.
+ * @see #getStateDescription()
+ */
+ @RemotableViewMethod
+ public void setStateDescription(@Nullable CharSequence stateDescription) {
+ if (mStateDescription == null) {
+ if (stateDescription == null) {
+ return;
+ }
+ } else if (mStateDescription.equals(stateDescription)) {
+ return;
+ }
+ mStateDescription = stateDescription;
+ if (!TextUtils.isEmpty(stateDescription)
+ && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
+ }
+
+ /**
* Sets the {@link View}'s content description.
* <p>
* A content description briefly describes the view and is primarily used
@@ -14414,8 +14466,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
- if (isVisible != oldVisible) {
- updateSystemGestureExclusionRects();
+ if (!getSystemGestureExclusionRects().isEmpty() && isVisible != oldVisible) {
+ postUpdateSystemGestureExclusionRects();
}
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index feba7bb7f195..f25206dce8ff 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -408,6 +408,7 @@ public interface ViewParent {
* or more of:
* <ul>
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index b2d70321178e..001ab6650551 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1475,6 +1475,9 @@ public interface WindowManager extends ViewManager {
* <p>When this flag is enabled for a window, it automatically sets
* the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p>
+ *
+ * <p>Note: For devices that support
+ * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE} this flag may be ignored.
*/
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
@@ -1494,6 +1497,10 @@ public interface WindowManager extends ViewManager {
* <p>When this flag is enabled for a window, it automatically sets
* the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.</p>
+ *
+ * <p>Note: For devices that support
+ * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE} this flag can be disabled
+ * by the car manufacturers.
*/
public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 584436542875..59e7bd236907 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.res.Configuration;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
@@ -93,6 +94,14 @@ class WindowlessWindowManager implements IWindowSession {
@Override
public void remove(android.view.IWindow window) {}
+ private boolean isOpaque(WindowManager.LayoutParams attrs) {
+ if (attrs.surfaceInsets.left != 0 || attrs.surfaceInsets.top != 0 ||
+ attrs.surfaceInsets.right != 0 || attrs.surfaceInsets.bottom != 0) {
+ return false;
+ }
+ return !PixelFormat.formatHasAlpha(attrs.format);
+ }
+
@Override
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
@@ -109,7 +118,11 @@ class WindowlessWindowManager implements IWindowSession {
"Invalid window token (never added or removed already)");
}
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(sc).setBufferSize(sc, requestedWidth, requestedHeight).apply();
+ t.show(sc).setBufferSize(sc,
+ requestedWidth + attrs.surfaceInsets.left + attrs.surfaceInsets.right,
+ requestedHeight + attrs.surfaceInsets.top + attrs.surfaceInsets.bottom)
+ .setOpaque(sc, isOpaque(attrs))
+ .apply();
outSurfaceControl.copyFrom(sc);
outFrame.set(0, 0, requestedWidth, requestedHeight);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index fd09a87aaa70..32b0f413321a 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -602,6 +602,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00000020;
/**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * state description of the node as returned by
+ * {@link AccessibilityNodeInfo#getStateDescription} changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000040;
+
+ /**
* Change type for {@link #TYPE_WINDOWS_CHANGED} event:
* The window was added.
*/
@@ -696,6 +703,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
CONTENT_CHANGE_TYPE_SUBTREE,
CONTENT_CHANGE_TYPE_TEXT,
CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION,
+ CONTENT_CHANGE_TYPE_STATE_DESCRIPTION,
CONTENT_CHANGE_TYPE_PANE_TITLE,
CONTENT_CHANGE_TYPE_PANE_APPEARED,
CONTENT_CHANGE_TYPE_PANE_DISAPPEARED
@@ -885,6 +893,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @return The bit mask of change types. One or more of:
* <ul>
* <li>{@link #CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link #CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}
* <li>{@link #CONTENT_CHANGE_TYPE_SUBTREE}
* <li>{@link #CONTENT_CHANGE_TYPE_TEXT}
* <li>{@link #CONTENT_CHANGE_TYPE_PANE_TITLE}
@@ -906,6 +915,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
switch (type) {
case CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION:
return "CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION";
+ case CONTENT_CHANGE_TYPE_STATE_DESCRIPTION:
+ return "CONTENT_CHANGE_TYPE_STATE_DESCRIPTION";
case CONTENT_CHANGE_TYPE_SUBTREE: return "CONTENT_CHANGE_TYPE_SUBTREE";
case CONTENT_CHANGE_TYPE_TEXT: return "CONTENT_CHANGE_TYPE_TEXT";
case CONTENT_CHANGE_TYPE_PANE_TITLE: return "CONTENT_CHANGE_TYPE_PANE_TITLE";
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 61debc8d8468..cf29ed712793 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -744,6 +744,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private CharSequence mHintText;
private CharSequence mError;
private CharSequence mPaneTitle;
+ private CharSequence mStateDescription;
private CharSequence mContentDescription;
private CharSequence mTooltipText;
private String mViewIdResourceName;
@@ -2723,6 +2724,15 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Get the state description of this node.
+ *
+ * @return the state description
+ */
+ public @Nullable CharSequence getStateDescription() {
+ return mStateDescription;
+ }
+
+ /**
* Gets the content description of this node.
*
* @return The content description.
@@ -2731,6 +2741,25 @@ public class AccessibilityNodeInfo implements Parcelable {
return mContentDescription;
}
+
+ /**
+ * Sets the state description of this node.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param stateDescription the state description of this node.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setStateDescription(@Nullable CharSequence stateDescription) {
+ enforceNotSealed();
+ mStateDescription = (stateDescription == null) ? null
+ : stateDescription.subSequence(0, stateDescription.length());
+ }
+
/**
* Sets the content description of this node.
* <p>
@@ -3373,6 +3402,10 @@ public class AccessibilityNodeInfo implements Parcelable {
fieldIndex++;
if (!Objects.equals(mError, DEFAULT.mError)) nonDefaultFields |= bitAt(fieldIndex);
fieldIndex++;
+ if (!Objects.equals(mStateDescription, DEFAULT.mStateDescription)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (!Objects.equals(mContentDescription, DEFAULT.mContentDescription)) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3505,6 +3538,7 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mText);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mHintText);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mError);
+ if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mStateDescription);
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeCharSequence(mContentDescription);
}
@@ -3582,6 +3616,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mOriginalText = other.mOriginalText;
mHintText = other.mHintText;
mError = other.mError;
+ mStateDescription = other.mStateDescription;
mContentDescription = other.mContentDescription;
mPaneTitle = other.mPaneTitle;
mTooltipText = other.mTooltipText;
@@ -3706,6 +3741,7 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) mText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mHintText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mError = parcel.readCharSequence();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mStateDescription = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
@@ -4004,6 +4040,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; text: ").append(mText);
builder.append("; error: ").append(mError);
builder.append("; maxTextLength: ").append(mMaxTextLength);
+ builder.append("; stateDescription: ").append(mStateDescription);
builder.append("; contentDescription: ").append(mContentDescription);
builder.append("; tooltipText: ").append(mTooltipText);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index 0dd2587feed5..e035c620d1fd 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -211,16 +211,19 @@ public final class ViewNode extends AssistStructure.ViewNode {
return mParentAutofillId;
}
+ @Nullable
@Override
public AutofillId getAutofillId() {
return mAutofillId;
}
+ @Nullable
@Override
public CharSequence getText() {
return mText != null ? mText.mText : null;
}
+ @Nullable
@Override
public String getClassName() {
return mClassName;
@@ -231,16 +234,19 @@ public final class ViewNode extends AssistStructure.ViewNode {
return mId;
}
+ @Nullable
@Override
public String getIdPackage() {
return mIdPackage;
}
+ @Nullable
@Override
public String getIdType() {
return mIdType;
}
+ @Nullable
@Override
public String getIdEntry() {
return mIdEntry;
@@ -341,21 +347,25 @@ public final class ViewNode extends AssistStructure.ViewNode {
return (mFlags & FLAGS_OPAQUE) != 0;
}
+ @Nullable
@Override
public CharSequence getContentDescription() {
return mContentDescription;
}
+ @Nullable
@Override
public Bundle getExtras() {
return mExtras;
}
+ @Nullable
@Override
public String getHint() {
return mText != null ? mText.mHint : null;
}
+ @Nullable
@Override
public String getHintIdEntry() {
return mHintIdEntry;
@@ -391,11 +401,13 @@ public final class ViewNode extends AssistStructure.ViewNode {
return mText != null ? mText.mTextStyle : 0;
}
+ @Nullable
@Override
public int[] getTextLineCharOffsets() {
return mText != null ? mText.mLineCharOffsets : null;
}
+ @Nullable
@Override
public int[] getTextLineBaselines() {
return mText != null ? mText.mLineBaselines : null;
@@ -426,6 +438,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
return mMaxLength;
}
+ @Nullable
@Override
public String getTextIdEntry() {
return mTextIdEntry;
@@ -451,6 +464,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
return mAutofillOptions;
}
+ @Nullable
@Override
public LocaleList getLocaleList() {
return mLocaleList;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 2f44d6ee88b1..1cb114856a4a 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -4109,7 +4109,7 @@ public class ListView extends AbsListView {
final int rowsCount = getCount();
final int selectionMode = getSelectionModeForAccessibility();
final CollectionInfo collectionInfo = CollectionInfo.obtain(
- rowsCount, 1, false, selectionMode);
+ -1, -1, false, selectionMode);
info.setCollectionInfo(collectionInfo);
if (rowsCount > 0) {
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index d3c69725b45b..f58b6d192fde 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -800,6 +800,7 @@ public final class Magnifier {
// The surface we allocate for the magnifier content + shadow.
private final SurfaceSession mSurfaceSession;
private final SurfaceControl mSurfaceControl;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private final Surface mSurface;
// The renderer used for the allocated surface.
private final ThreadedRenderer.SimpleRenderer mRenderer;
@@ -1081,16 +1082,16 @@ public final class Magnifier {
return;
}
// Show or move the window at the content draw frame.
- SurfaceControl.openTransaction();
- mSurfaceControl.deferTransactionUntil(mSurface, frame);
+ mTransaction.deferTransactionUntilSurface(mSurfaceControl, mSurface, frame);
if (updateWindowPosition) {
- mSurfaceControl.setPosition(pendingX, pendingY);
+ mTransaction.setPosition(mSurfaceControl, pendingX, pendingY);
}
if (firstDraw) {
- mSurfaceControl.setLayer(SURFACE_Z);
- mSurfaceControl.show();
+ mTransaction.setLayer(mSurfaceControl, SURFACE_Z)
+ .show(mSurfaceControl);
+
}
- SurfaceControl.closeTransaction();
+ mTransaction.apply();
};
mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
} else {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index de77aaae1653..cae1f3831b4a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,6 +24,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -120,6 +121,7 @@ import com.google.android.collect.Lists;
import java.io.IOException;
import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@@ -178,6 +180,20 @@ public class ChooserActivity extends ResolverActivity {
private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true;
private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
+ public static final int TARGET_TYPE_DEFAULT = 0;
+ public static final int TARGET_TYPE_CHOOSER_TARGET = 1;
+ public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2;
+ public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3;
+
+ @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = {
+ TARGET_TYPE_DEFAULT,
+ TARGET_TYPE_CHOOSER_TARGET,
+ TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER,
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ShareTargetType {}
+
/**
* The transition time between placeholders for direct share to a message
* indicating that non are available.
@@ -218,9 +234,9 @@ public class ChooserActivity extends ResolverActivity {
private int mCurrAvailableWidth = 0;
/** {@link ChooserActivity#getBaseScore} */
- private static final float CALLER_TARGET_SCORE_BOOST = 900.f;
+ public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
/** {@link ChooserActivity#getBaseScore} */
- private static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f;
+ public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f;
private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
// TODO: Update to handle landscape instead of using static value
private static final int MAX_RANKED_TARGETS = 4;
@@ -443,7 +459,7 @@ public class ChooserActivity extends ResolverActivity {
}
if (sri.resultTargets != null) {
mChooserListAdapter.addServiceResults(sri.originalTarget,
- sri.resultTargets, false);
+ sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
}
unbindService(sri.connection);
sri.connection.destroy();
@@ -474,7 +490,7 @@ public class ChooserActivity extends ResolverActivity {
final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
if (resultInfo.resultTargets != null) {
mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
- resultInfo.resultTargets, true);
+ resultInfo.resultTargets, msg.arg1);
}
break;
@@ -714,7 +730,13 @@ public class ChooserActivity extends ResolverActivity {
/**
* Returns true if app prediction service is defined and the component exists on device.
*/
- private boolean isAppPredictionServiceAvailable() {
+ @VisibleForTesting
+ public boolean isAppPredictionServiceAvailable() {
+ if (getPackageManager().getAppPredictionServicePackageName() == null) {
+ // Default AppPredictionService is not defined.
+ return false;
+ }
+
final String appPredictionServiceName =
getString(R.string.config_defaultAppPredictionService);
if (appPredictionServiceName == null) {
@@ -797,6 +819,11 @@ public class ChooserActivity extends ResolverActivity {
clipboardManager.setPrimaryClip(clipData);
Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
+ // Log share completion via copy
+ LogMaker targetLogMaker = new LogMaker(
+ MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1);
+ getMetricsLogger().write(targetLogMaker);
+
finish();
}
}
@@ -1209,7 +1236,7 @@ public class ChooserActivity extends ResolverActivity {
mChooserListAdapter = (ChooserListAdapter) adapter;
if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets),
- false);
+ TARGET_TYPE_DEFAULT);
}
mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
if (listView != null) {
@@ -1555,33 +1582,32 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ // If |appTargets| is not null, results are from AppPredictionService and already sorted.
+ final int shortcutType = (appTargets == null ? TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER :
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+
// Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
// for direct share targets. After ShareSheet is refactored we should use the
// ShareShortcutInfos directly.
boolean resultMessageSent = false;
for (int i = 0; i < driList.size(); i++) {
- List<ChooserTarget> chooserTargets = new ArrayList<>();
+ List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
for (int j = 0; j < resultList.size(); j++) {
if (driList.get(i).getResolvedComponentName().equals(
resultList.get(j).getTargetComponent())) {
- ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
- // Incoming results are ordered but without a score. Create a score
- // based on the index in order to be sorted appropriately when joined
- // with legacy direct share api results.
- float score = Math.max(1.0f - (0.05f * j), 0.0f);
- ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score);
- chooserTargets.add(chooserTarget);
- if (mDirectShareAppTargetCache != null && appTargets != null) {
- mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
- }
+ matchingShortcuts.add(resultList.get(j));
}
}
- if (chooserTargets.isEmpty()) {
+ if (matchingShortcuts.isEmpty()) {
continue;
}
+ List<ChooserTarget> chooserTargets = convertToChooserTarget(
+ matchingShortcuts, resultList, appTargets, shortcutType);
+
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
msg.obj = new ServiceResultInfo(driList.get(i), chooserTargets, null);
+ msg.arg1 = shortcutType;
mChooserHandler.sendMessage(msg);
resultMessageSent = true;
}
@@ -1615,23 +1641,69 @@ public class ChooserActivity extends ResolverActivity {
return false;
}
- private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut,
- float score) {
- ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
- Bundle extras = new Bundle();
- extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
- return new ChooserTarget(
- // The name of this target.
- shortcutInfo.getShortLabel(),
- // Don't load the icon until it is selected to be shown
- null,
- // The ranking score for this target (0.0-1.0); the system will omit items with low
- // scores when there are too many Direct Share items.
- score,
- // The name of the component to be launched if this target is chosen.
- shareShortcut.getTargetComponent().clone(),
- // The extra values here will be merged into the Intent when this target is chosen.
- extras);
+ /**
+ * Converts a list of ShareShortcutInfos to ChooserTargets.
+ * @param matchingShortcuts List of shortcuts, all from the same package, that match the current
+ * share intent filter.
+ * @param allShortcuts List of all the shortcuts from all the packages on the device that are
+ * returned for the current sharing action.
+ * @param allAppTargets List of AppTargets. Null if the results are not from prediction service.
+ * @param shortcutType One of the values TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER or
+ * TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
+ * @return A list of ChooserTargets sorted by score in descending order.
+ */
+ @VisibleForTesting
+ @NonNull
+ public List<ChooserTarget> convertToChooserTarget(
+ @NonNull List<ShortcutManager.ShareShortcutInfo> matchingShortcuts,
+ @NonNull List<ShortcutManager.ShareShortcutInfo> allShortcuts,
+ @Nullable List<AppTarget> allAppTargets, @ShareTargetType int shortcutType) {
+ // A set of distinct scores for the matched shortcuts. We use index of a rank in the sorted
+ // list instead of the actual rank value when converting a rank to a score.
+ List<Integer> scoreList = new ArrayList<>();
+ if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
+ for (int i = 0; i < matchingShortcuts.size(); i++) {
+ int shortcutRank = matchingShortcuts.get(i).getShortcutInfo().getRank();
+ if (!scoreList.contains(shortcutRank)) {
+ scoreList.add(shortcutRank);
+ }
+ }
+ Collections.sort(scoreList);
+ }
+
+ List<ChooserTarget> chooserTargetList = new ArrayList<>(matchingShortcuts.size());
+ for (int i = 0; i < matchingShortcuts.size(); i++) {
+ ShortcutInfo shortcutInfo = matchingShortcuts.get(i).getShortcutInfo();
+ int indexInAllShortcuts = allShortcuts.indexOf(matchingShortcuts.get(i));
+
+ float score;
+ if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
+ // Incoming results are ordered. Create a score based on index in the original list.
+ score = Math.max(1.0f - (0.01f * indexInAllShortcuts), 0.0f);
+ } else {
+ // Create a score based on the rank of the shortcut.
+ int rankIndex = scoreList.indexOf(shortcutInfo.getRank());
+ score = Math.max(1.0f - (0.01f * rankIndex), 0.0f);
+ }
+
+ Bundle extras = new Bundle();
+ extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
+ ChooserTarget chooserTarget = new ChooserTarget(shortcutInfo.getShortLabel(),
+ null, // Icon will be loaded later if this target is selected to be shown.
+ score, matchingShortcuts.get(i).getTargetComponent().clone(), extras);
+
+ chooserTargetList.add(chooserTarget);
+ if (mDirectShareAppTargetCache != null && allAppTargets != null) {
+ mDirectShareAppTargetCache.put(chooserTarget,
+ allAppTargets.get(indexInAllShortcuts));
+ }
+ }
+
+ // Sort ChooserTargets by score in descending order
+ Comparator<ChooserTarget> byScore =
+ (ChooserTarget a, ChooserTarget b) -> -Float.compare(a.getScore(), b.getScore());
+ Collections.sort(chooserTargetList, byScore);
+ return chooserTargetList;
}
private String convertServiceName(String packageName, String serviceName) {
@@ -1666,10 +1738,6 @@ public class ChooserActivity extends ResolverActivity {
mServiceConnections.clear();
}
- public void onSetupVoiceInteraction() {
- // Do nothing. We'll send the voice stuff ourselves.
- }
-
private void logDirectShareTargetReceived(int logCategory) {
final long queryTime =
logCategory == MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER
@@ -1727,8 +1795,7 @@ public class ChooserActivity extends ResolverActivity {
if (!mIsAppPredictorComponentAvailable) {
return null;
}
- if (mAppPredictor == null
- && getPackageManager().getAppPredictionServicePackageName() != null) {
+ if (mAppPredictor == null) {
final IntentFilter filter = getTargetIntentFilter();
Bundle extras = new Bundle();
extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
@@ -2676,7 +2743,7 @@ public class ChooserActivity extends ResolverActivity {
* if score is too low.
*/
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
- boolean isShortcutResult) {
+ @ShareTargetType int targetType) {
if (DEBUG) {
Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
+ " targets");
@@ -2686,9 +2753,12 @@ public class ChooserActivity extends ResolverActivity {
return;
}
- final float baseScore = getBaseScore(origTarget, isShortcutResult);
+ final float baseScore = getBaseScore(origTarget, targetType);
Collections.sort(targets, mBaseTargetComparator);
+ final boolean isShortcutResult =
+ (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
+ || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp
: MAX_CHOOSER_TARGETS_PER_APP;
float lastScore = 0;
@@ -2739,17 +2809,17 @@ public class ChooserActivity extends ResolverActivity {
* <li>Legacy direct share targets
* </ol>
*/
- private float getBaseScore(DisplayResolveInfo target, boolean isShortcutResult) {
+ public float getBaseScore(DisplayResolveInfo target, @ShareTargetType int targetType) {
if (target == null) {
return CALLER_TARGET_SCORE_BOOST;
}
- if (isShortcutResult && getAppPredictorForDirectShareIfEnabled() != null) {
+ if (targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
return SHORTCUT_TARGET_SCORE_BOOST;
}
float score = super.getScore(target);
- if (isShortcutResult) {
+ if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
return score * SHORTCUT_TARGET_SCORE_BOOST;
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 58ce03baa136..407a85f1bb05 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -361,9 +361,6 @@ public class ResolverActivity extends Activity {
initSuspendedColorMatrix();
- if (isVoiceInteraction()) {
- onSetupVoiceInteraction();
- }
final Set<String> categories = intent.getCategories();
MetricsLogger.action(this, mAdapter.hasFilteredItem()
? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
@@ -442,24 +439,21 @@ public class ResolverActivity extends Activity {
mSuspendedMatrixColorFilter = new ColorMatrixColorFilter(matrix);
}
- /**
- * Perform any initialization needed for voice interaction.
- */
- public void onSetupVoiceInteraction() {
- // Do it right now. Subclasses may delay this and send it later.
- sendVoiceChoicesIfNeeded();
- }
-
public void sendVoiceChoicesIfNeeded() {
if (!isVoiceInteraction()) {
// Clearly not needed.
return;
}
-
final Option[] options = new Option[mAdapter.getCount()];
for (int i = 0, N = options.length; i < N; i++) {
- options[i] = optionForChooserTarget(mAdapter.getItem(i), i);
+ TargetInfo target = mAdapter.getItem(i);
+ if (target == null) {
+ // If this occurs, a new set of targets is being loaded. Let that complete,
+ // and have the next call to send voice choices proceed instead.
+ return;
+ }
+ options[i] = optionForChooserTarget(target, i);
}
mPickOptionRequest = new PickTargetOptionRequest(
@@ -1872,7 +1866,7 @@ public class ResolverActivity extends Activity {
}
}
-
+ sendVoiceChoicesIfNeeded();
postListReadyRunnable();
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index a7f7fe19bb4d..7cbacade5797 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -27,5 +27,6 @@ oneway interface IAppWidgetHost {
void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
void providersChanged();
void viewDataChanged(int appWidgetId, int viewId);
+ void appWidgetRemoved(int appWidgetId);
}
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
deleted file mode 100644
index a72a2f5e1be3..000000000000
--- a/core/java/com/android/internal/os/AtomicFile.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.FileUtils;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Helper class for performing atomic operations on a file, by creating a
- * backup file until a write has successfully completed.
- * <p>
- * Atomic file guarantees file integrity by ensuring that a file has
- * been completely written and sync'd to disk before removing its backup.
- * As long as the backup file exists, the original file is considered
- * to be invalid (left over from a previous attempt to write the file).
- * </p><p>
- * Atomic file does not confer any file locking semantics.
- * Do not use this class when the file may be accessed or modified concurrently
- * by multiple threads or processes. The caller is responsible for ensuring
- * appropriate mutual exclusion invariants whenever it accesses the file.
- * </p>
- */
-public final class AtomicFile {
- private final File mBaseName;
- private final File mBackupName;
-
- @UnsupportedAppUsage
- public AtomicFile(File baseName) {
- mBaseName = baseName;
- mBackupName = new File(baseName.getPath() + ".bak");
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public File getBaseFile() {
- return mBaseName;
- }
-
- @UnsupportedAppUsage
- public FileOutputStream startWrite() throws IOException {
- // Rename the current file so it may be used as a backup during the next read
- if (mBaseName.exists()) {
- if (!mBackupName.exists()) {
- if (!mBaseName.renameTo(mBackupName)) {
- Log.w("AtomicFile", "Couldn't rename file " + mBaseName
- + " to backup file " + mBackupName);
- }
- } else {
- mBaseName.delete();
- }
- }
- FileOutputStream str = null;
- try {
- str = new FileOutputStream(mBaseName);
- } catch (FileNotFoundException e) {
- File parent = mBaseName.getParentFile();
- if (!parent.mkdir()) {
- throw new IOException("Couldn't create directory " + mBaseName);
- }
- FileUtils.setPermissions(
- parent.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
- try {
- str = new FileOutputStream(mBaseName);
- } catch (FileNotFoundException e2) {
- throw new IOException("Couldn't create " + mBaseName);
- }
- }
- return str;
- }
-
- @UnsupportedAppUsage
- public void finishWrite(FileOutputStream str) {
- if (str != null) {
- FileUtils.sync(str);
- try {
- str.close();
- mBackupName.delete();
- } catch (IOException e) {
- Log.w("AtomicFile", "finishWrite: Got exception:", e);
- }
- }
- }
-
- @UnsupportedAppUsage
- public void failWrite(FileOutputStream str) {
- if (str != null) {
- FileUtils.sync(str);
- try {
- str.close();
- mBaseName.delete();
- mBackupName.renameTo(mBaseName);
- } catch (IOException e) {
- Log.w("AtomicFile", "failWrite: Got exception:", e);
- }
- }
- }
-
- @UnsupportedAppUsage
- public FileOutputStream openAppend() throws IOException {
- try {
- return new FileOutputStream(mBaseName, true);
- } catch (FileNotFoundException e) {
- throw new IOException("Couldn't append " + mBaseName);
- }
- }
-
- @UnsupportedAppUsage
- public void truncate() throws IOException {
- try {
- FileOutputStream fos = new FileOutputStream(mBaseName);
- FileUtils.sync(fos);
- fos.close();
- } catch (FileNotFoundException e) {
- throw new IOException("Couldn't append " + mBaseName);
- } catch (IOException e) {
- }
- }
-
- public boolean exists() {
- return mBaseName.exists() || mBackupName.exists();
- }
-
- public void delete() {
- mBaseName.delete();
- mBackupName.delete();
- }
-
- @UnsupportedAppUsage
- public FileInputStream openRead() throws FileNotFoundException {
- if (mBackupName.exists()) {
- mBaseName.delete();
- mBackupName.renameTo(mBaseName);
- }
- return new FileInputStream(mBaseName);
- }
-
- @UnsupportedAppUsage
- public byte[] readFully() throws IOException {
- FileInputStream stream = openRead();
- try {
- int pos = 0;
- int avail = stream.available();
- byte[] data = new byte[avail];
- while (true) {
- int amt = stream.read(data, pos, data.length-pos);
- //Log.i("foo", "Read " + amt + " bytes at " + pos
- // + " of avail " + data.length);
- if (amt <= 0) {
- //Log.i("foo", "**** FINISHED READING: pos=" + pos
- // + " len=" + data.length);
- return data;
- }
- pos += amt;
- avail = stream.available();
- if (avail > data.length-pos) {
- byte[] newData = new byte[pos+avail];
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- } finally {
- stream.close();
- }
- }
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 392f0748ca0d..bdb65f33ad1a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.ArraySet;
+import android.util.AtomicFile;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8503ab82ac48..8978414034ff 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -60,11 +60,13 @@ import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
+import android.telephony.ModemActivityInfo.TransmitPower;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
@@ -10975,7 +10977,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
private ModemActivityInfo mLastModemActivityInfo =
- new ModemActivityInfo(0, 0, 0, new int[0], 0, 0);
+ new ModemActivityInfo(0, 0, 0, new int[0], 0);
private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
if (activityInfo == null) {
@@ -10983,15 +10985,14 @@ public class BatteryStatsImpl extends BatteryStats {
}
int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
- txTimeMs[i] = activityInfo.getTxTimeMillis()[i]
- - mLastModemActivityInfo.getTxTimeMillis()[i];
+ txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis()
+ - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis();
}
ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
txTimeMs,
- activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(),
- activityInfo.getEnergyUsed() - mLastModemActivityInfo.getEnergyUsed());
+ activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis());
mLastModemActivityInfo = activityInfo;
return deltaInfo;
}
@@ -11034,10 +11035,11 @@ public class BatteryStatsImpl extends BatteryStats {
deltaInfo.getIdleTimeMillis());
mModemActivity.getSleepTimeCounter().addCountLocked(
deltaInfo.getSleepTimeMillis());
- mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis());
+ mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(deltaInfo.getTxTimeMillis()[lvl]);
+ .addCountLocked(deltaInfo.getTransmitPowerInfo()
+ .get(lvl).getTimeInMillis());
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11049,13 +11051,13 @@ public class BatteryStatsImpl extends BatteryStats {
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
+ deltaInfo.getIdleTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
- + deltaInfo.getRxTimeMillis() *
+ + deltaInfo.getReceiveTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
- int[] txTimeMs = deltaInfo.getTxTimeMillis();
- for (int i = 0; i < Math.min(txTimeMs.length,
+ List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
+ for (int i = 0; i < Math.min(txPowerInfo.size(),
SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
- energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower(
- PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
+ .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
@@ -11144,15 +11146,16 @@ public class BatteryStatsImpl extends BatteryStats {
ControllerActivityCounterImpl activityCounter =
u.getOrCreateModemControllerActivityLocked();
if (totalRxPackets > 0 && entry.rxPackets > 0) {
- final long rxMs = (entry.rxPackets * deltaInfo.getRxTimeMillis())
- / totalRxPackets;
+ final long rxMs = (entry.rxPackets
+ * deltaInfo.getReceiveTimeMillis()) / totalRxPackets;
activityCounter.getRxTimeCounter().addCountLocked(rxMs);
}
if (totalTxPackets > 0 && entry.txPackets > 0) {
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
long txMs =
- entry.txPackets * deltaInfo.getTxTimeMillis()[lvl];
+ entry.txPackets * deltaInfo.getTransmitPowerInfo()
+ .get(lvl).getTimeInMillis();
txMs /= totalTxPackets;
activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
}
@@ -11183,15 +11186,16 @@ public class BatteryStatsImpl extends BatteryStats {
if (activityInfo == null) {
return;
}
- int[] txTimeMs = activityInfo.getTxTimeMillis();
- if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo();
+ if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
return;
}
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
int levelMaxTimeSpent = 0;
- for (int i = 1; i < txTimeMs.length; i++) {
- if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ for (int i = 1; i < txPowerInfo.size(); i++) {
+ if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
+ .getTimeInMillis()) {
levelMaxTimeSpent = i;
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b5d494540f8a..3be1a1aefe57 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -460,7 +460,7 @@ public class ZygoteInit {
ZygoteHooks.gcAndFinalize();
}
- private static boolean profileSystemServer() {
+ private static boolean shouldProfileSystemServer() {
boolean defaultValue = SystemProperties.getBoolean("dalvik.vm.profilesystemserver",
/*default=*/ false);
// Can't use DeviceConfig since it's not initialized at this point.
@@ -492,7 +492,7 @@ public class ZygoteInit {
}
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
- if (profileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
+ if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
try {
Log.d(TAG, "Preparing system server profile");
prepareSystemServerProfile(systemServerClasspath);
@@ -749,7 +749,7 @@ public class ZygoteInit {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
- + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+ + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
@@ -765,8 +765,7 @@ public class ZygoteInit {
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
- if (profileSystemServer()) {
-
+ if (shouldProfileSystemServer()) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 981d0bb1cd69..b02563a67503 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -804,7 +804,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
updateElevation();
mAllowUpdateElevation = true;
- if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
+ if (changed
+ && (mResizeMode == RESIZE_MODE_DOCKED_DIVIDER
+ || mDrawLegacyNavigationBarBackground)) {
getViewRootImpl().requestInvalidateRootRenderNode();
}
}
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 3cb6919819ab..b54e5fb56712 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -229,6 +229,7 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
* default (undefined) change type or one or more of:
* <ul>
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
* <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 7779f55518f0..5a0f16e589ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,10 +42,6 @@ cc_library_shared {
],
include_dirs: [
- // we need to access the private Bionic header
- // <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
- "bionic/libc/private",
-
"external/skia/include/private",
"frameworks/base/media/jni",
"system/media/camera/include",
@@ -277,6 +273,7 @@ cc_library_shared {
"libnativewindow",
],
generated_sources: ["android_util_StatsLogInternal.cpp"],
+ header_libs: ["bionic_libc_platform_headers"],
},
host: {
cflags: [
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
index 5f830382bd01..ca8b8de8d3ad 100644
--- a/core/jni/android_app_ActivityThread.cpp
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -23,7 +23,7 @@
#include "core_jni_helpers.h"
#include <unistd.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
namespace android {
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index 076e99dd1fba..2ca4500991fa 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -23,7 +23,7 @@
#include "core_jni_helpers.h"
#include <android-base/logging.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include <utils/Log.h>
#include <utils/String8.h>
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index f87163bb8379..bc69735d7453 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -1023,22 +1023,38 @@ static void android_hardware_Camera_enableFocusMoveCallback(JNIEnv *env, jobject
}
}
-static int32_t android_hardware_Camera_setAudioRestriction(
+static void android_hardware_Camera_setAudioRestriction(
JNIEnv *env, jobject thiz, jint mode)
{
ALOGV("setAudioRestriction");
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) {
jniThrowRuntimeException(env, "camera has been disconnected");
- return -1;
+ return;
}
int32_t ret = camera->setAudioRestriction(mode);
if (ret < 0) {
jniThrowRuntimeException(env, "Illegal argument or low-level eror");
+ return;
+ }
+}
+
+static int32_t android_hardware_Camera_getAudioRestriction(
+ JNIEnv *env, jobject thiz)
+{
+ ALOGV("getAudioRestriction");
+ sp<Camera> camera = get_native_camera(env, thiz, NULL);
+ if (camera == 0) {
+ jniThrowRuntimeException(env, "camera has been disconnected");
return -1;
}
+ int32_t ret = camera->getGlobalAudioRestriction();
+ if (ret < 0) {
+ jniThrowRuntimeException(env, "Illegal argument or low-level eror");
+ return -1;
+ }
return ret;
}
@@ -1127,8 +1143,11 @@ static const JNINativeMethod camMethods[] = {
"(I)V",
(void *)android_hardware_Camera_enableFocusMoveCallback},
{ "setAudioRestriction",
- "(I)I",
+ "(I)V",
(void *)android_hardware_Camera_setAudioRestriction},
+ { "getAudioRestriction",
+ "()I",
+ (void *)android_hardware_Camera_getAudioRestriction},
};
struct field {
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 03057dc5e602..0002f8b4048a 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -606,12 +606,12 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz,
goto exit;
}
memory = memoryDealer->allocate(offset + size);
- if (memory == 0 || memory->pointer() == NULL) {
+ if (memory == 0 || memory->unsecurePointer() == NULL) {
status = SOUNDTRIGGER_STATUS_ERROR;
goto exit;
}
- nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
+ nSoundModel = (struct sound_trigger_sound_model *)memory->unsecurePointer();
nSoundModel->type = type;
nSoundModel->uuid = nUuid;
@@ -737,18 +737,18 @@ android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
return SOUNDTRIGGER_STATUS_ERROR;
}
sp<IMemory> memory = memoryDealer->allocate(totalSize);
- if (memory == 0 || memory->pointer() == NULL) {
+ if (memory == 0 || memory->unsecurePointer() == NULL) {
return SOUNDTRIGGER_STATUS_ERROR;
}
if (dataSize != 0) {
- memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config),
+ memcpy((char *)memory->unsecurePointer() + sizeof(struct sound_trigger_recognition_config),
nData,
dataSize);
env->ReleaseByteArrayElements(jData, nData, 0);
}
env->DeleteLocalRef(jData);
struct sound_trigger_recognition_config *config =
- (struct sound_trigger_recognition_config *)memory->pointer();
+ (struct sound_trigger_recognition_config *)memory->unsecurePointer();
config->data_size = dataSize;
config->data_offset = sizeof(struct sound_trigger_recognition_config);
config->capture_requested = env->GetBooleanField(jConfig,
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index daa63475f706..c5049ecd3784 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -649,7 +649,7 @@ static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
- memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
+ memcpy(track->sharedBuffer()->unsecurePointer(), data + offsetInSamples, sizeInBytes);
written = sizeInBytes;
}
if (written >= 0) {
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index cf8df287a6db..9c52a6433360 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -34,7 +34,7 @@
#include <vector>
#include <android-base/logging.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include <debuggerd/client.h>
#include <log/log.h>
#include <utils/misc.h>
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index cbae2da04281..b6427c9aa01c 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -152,7 +152,7 @@ status_t JHwBinder::onTransact(
uint32_t flags,
TransactCallback callback) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- bool isOneway = (flags & TF_ONE_WAY) != 0;
+ bool isOneway = (flags & IBinder::FLAG_ONEWAY) != 0;
ScopedLocalRef<jobject> replyObj(env, nullptr);
sp<JHwParcel> replyContext = nullptr;
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 8553a2c24826..af34e7b7a7ff 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "InputChannel-JNI"
+#include "android-base/stringprintf.h"
#include <nativehelper/JNIHelp.h>
#include "nativehelper/scoped_utf_chars.h"
#include <android_runtime/AndroidRuntime.h>
@@ -60,7 +61,7 @@ private:
// ----------------------------------------------------------------------------
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
- mInputChannel(inputChannel), mDisposeCallback(NULL) {
+ mInputChannel(inputChannel), mDisposeCallback(nullptr) {
}
NativeInputChannel::~NativeInputChannel() {
@@ -74,8 +75,8 @@ void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callb
void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
if (mDisposeCallback) {
mDisposeCallback(env, obj, mInputChannel, mDisposeData);
- mDisposeCallback = NULL;
- mDisposeData = NULL;
+ mDisposeCallback = nullptr;
+ mDisposeData = nullptr;
}
}
@@ -96,14 +97,14 @@ static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject
sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
- return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
+ return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
}
void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
- if (nativeInputChannel == NULL) {
+ if (nativeInputChannel == nullptr) {
ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
} else {
nativeInputChannel->setDisposeCallback(callback, data);
@@ -131,27 +132,27 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv*
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
- String8 message;
- message.appendFormat("Could not open input channel pair. status=%d", result);
- jniThrowRuntimeException(env, message.string());
- return NULL;
+ std::string message = android::base::StringPrintf(
+ "Could not open input channel pair : %s", strerror(-result));
+ jniThrowRuntimeException(env, message.c_str());
+ return nullptr;
}
- jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
+ jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
- return NULL;
+ return nullptr;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
@@ -170,7 +171,7 @@ static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jb
nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
delete nativeInputChannel;
}
}
@@ -179,14 +180,14 @@ static void android_view_InputChannel_nativeRelease(JNIEnv* env, jobject obj, jb
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (nativeInputChannel) {
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
delete nativeInputChannel;
}
}
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
- if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
+ if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
@@ -195,12 +196,12 @@ static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
- android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
}
static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
- if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
+ if (android_view_InputChannel_getNativeInputChannel(env, obj) != nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"This object already has a native input channel.");
return;
@@ -222,25 +223,26 @@ static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject
static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
- if (parcel) {
- NativeInputChannel* nativeInputChannel =
- android_view_InputChannel_getNativeInputChannel(env, obj);
- if (nativeInputChannel) {
- sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
-
- parcel->writeInt32(1);
- inputChannel->write(*parcel);
- } else {
- parcel->writeInt32(0);
- }
+ if (parcel == nullptr) {
+ ALOGE("Could not obtain parcel for Java object");
+ return;
+ }
+
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (!nativeInputChannel) {
+ parcel->writeInt32(0); // not initialized
+ return;
}
+ parcel->writeInt32(1); // initialized
+ nativeInputChannel->getInputChannel()->write(*parcel);
}
static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (! nativeInputChannel) {
- return NULL;
+ return nullptr;
}
jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
@@ -250,10 +252,24 @@ static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj)
static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
- if (nativeInputChannel) {
- android_view_InputChannel_setNativeInputChannel(env, otherObj,
- new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
+ if (nativeInputChannel == nullptr) {
+ jniThrowRuntimeException(env, "InputChannel has no valid NativeInputChannel");
+ return;
+ }
+
+ sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
+ if (inputChannel == nullptr) {
+ jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
+ return;
+ }
+ sp<InputChannel> dupInputChannel = inputChannel->dup();
+ if (dupInputChannel == nullptr) {
+ std::string message = android::base::StringPrintf(
+ "Could not duplicate input channel %s", inputChannel->getName().c_str());
+ jniThrowRuntimeException(env, message.c_str());
}
+ android_view_InputChannel_setNativeInputChannel(env, otherObj,
+ new NativeInputChannel(dupInputChannel));
}
static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index d5b875b85d7d..d42a48a1f899 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -73,7 +73,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include <cutils/ashmem.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index 57ced09240f2..7fa0ff64f8bf 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -32,7 +32,7 @@ message ConfigurationProto {
optional float font_scale = 1;
optional uint32 mcc = 2;
optional uint32 mnc = 3 [ (.android.privacy).dest = DEST_EXPLICIT ];
- repeated LocaleProto locales = 4;
+ repeated LocaleProto locales = 4 [deprecated = true];
optional uint32 screen_layout = 5;
optional uint32 color_mode = 6;
optional uint32 touchscreen = 7;
@@ -48,6 +48,7 @@ message ConfigurationProto {
optional uint32 smallest_screen_width_dp = 17;
optional uint32 density_dpi = 18;
optional .android.app.WindowConfigurationProto window_configuration = 19;
+ optional string locale_list = 20;
}
/**
diff --git a/core/proto/android/content/locale.proto b/core/proto/android/content/locale.proto
index bae6ec141981..a8f2a1334038 100644
--- a/core/proto/android/content/locale.proto
+++ b/core/proto/android/content/locale.proto
@@ -22,6 +22,7 @@ import "frameworks/base/core/proto/android/privacy.proto";
package android.content;
message LocaleProto {
+ option deprecated = true;
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional string language = 1;
diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto
index 655b5e326019..f648912d36eb 100644
--- a/core/proto/android/stats/docsui/docsui_enums.proto
+++ b/core/proto/android/stats/docsui/docsui_enums.proto
@@ -184,6 +184,8 @@ enum SearchType {
TYPE_CHIP_DOCS = 4;
TYPE_SEARCH_HISTORY = 5;
TYPE_SEARCH_STRING = 6;
+ TYPE_CHIP_LARGE_FILES = 7;
+ TYPE_CHIP_FROM_THIS_WEEK = 8;
}
enum SearchMode {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9738759c5f3e..e23c75e2be0f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2008,6 +2008,12 @@
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows read access to emergency number information for ongoing calls or SMS
+ sessions.
+ @hide Used internally. -->
+ <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
corresponds to a device SIM.
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 797d75f5a54f..839aaac16c7a 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -95,6 +95,7 @@
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"Voicemail messages"</string>
<string name="notification_channel_wfc" msgid="2130802501654254801">"Wi-Fi Calling"</string>
<string name="notification_channel_sim" msgid="4052095493875188564">"SIM status"</string>
+ <string name="notification_channel_sim_high_prio" msgid="1787666807724243207">"High priority SIM status"</string>
<string name="peerTtyModeFull" msgid="6165351790010341421">"Peer requested TTY Mode FULL"</string>
<string name="peerTtyModeHco" msgid="5728602160669216784">"Peer requested TTY Mode HCO"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"Peer requested TTY Mode VCO"</string>
@@ -1256,10 +1257,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string>
- <string name="wifi_suggestion_title" msgid="9099832833531486167">"Connect to Wi‑Fi networks?"</string>
- <string name="wifi_suggestion_content" msgid="5883181205841582873">"Suggested by <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string>
- <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"Allow suggested Wi‑Fi networks?"</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> suggested networks. Device may connect automatically."</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Allow"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"No, thanks"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7ea76500e4ea..7961a865cd53 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -95,6 +95,7 @@
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎Voicemail messages‎‏‎‎‏‎"</string>
<string name="notification_channel_wfc" msgid="2130802501654254801">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎Wi-Fi calling‎‏‎‎‏‎"</string>
<string name="notification_channel_sim" msgid="4052095493875188564">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎SIM status‎‏‎‎‏‎"</string>
+ <string name="notification_channel_sim_high_prio" msgid="1787666807724243207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎High priority SIM status‎‏‎‎‏‎"</string>
<string name="peerTtyModeFull" msgid="6165351790010341421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎Peer requested TTY Mode FULL‎‏‎‎‏‎"</string>
<string name="peerTtyModeHco" msgid="5728602160669216784">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎Peer requested TTY Mode HCO‎‏‎‎‏‎"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎Peer requested TTY Mode VCO‎‏‎‎‏‎"</string>
@@ -1256,10 +1257,10 @@
<string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎Tap to see all networks‎‏‎‎‏‎"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎Connect‎‏‎‎‏‎"</string>
<string name="wifi_available_action_all_networks" msgid="4368435796357931006">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎All networks‎‏‎‎‏‎"</string>
- <string name="wifi_suggestion_title" msgid="9099832833531486167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎Connect to Wi‑Fi networks?‎‏‎‎‏‎"</string>
- <string name="wifi_suggestion_content" msgid="5883181205841582873">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎Suggested by ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎Yes‎‏‎‎‏‎"</string>
- <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎No‎‏‎‎‏‎"</string>
+ <string name="wifi_suggestion_title" msgid="6396033039578436801">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎Allow suggested Wi‑Fi networks?‎‏‎‎‏‎"</string>
+ <string name="wifi_suggestion_content" msgid="5603992011371520746">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ suggested networks. Device may connect automatically.‎‏‎‎‏‎"</string>
+ <string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎Allow‎‏‎‎‏‎"</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎No thanks‎‏‎‎‏‎"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎Wi‑Fi will turn on automatically‎‏‎‎‏‎"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎When you\'re near a high quality saved network‎‏‎‎‏‎"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎Don\'t turn back on‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 1346b8631935..0229cf5a0ed4 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -30,7 +30,7 @@
<string name="untitled" msgid="4638956954852782576">"&lt;Sin título&gt;"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(No hay número de teléfono)"</string>
<string name="unknownName" msgid="6867811765370350269">"Desconocido"</string>
- <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Correo de voz"</string>
+ <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Buzón de voz"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"Problema de conexión o código incorrecto de MMI."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"La operación está limitada a números de marcación fija."</string>
@@ -944,7 +944,7 @@
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite que la aplicación modifique el historial o los favoritos del navegador almacenados en el dispositivo. La aplicación puede utilizar este permiso para borrar o modificar los datos del navegador. Nota: Este permiso no puede ser utilizado por navegadores externos ni otras aplicaciones que tengan funciones de navegación por Internet."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"programar una alarma"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de alarma instalada. Es posible que algunas aplicaciones de alarma no incluyan esta función."</string>
- <string name="permlab_addVoicemail" msgid="5525660026090959044">"agregar correo de voz"</string>
+ <string name="permlab_addVoicemail" msgid="5525660026090959044">"agregar buzón de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación agregue mensajes a la bandeja de entrada de tu buzón de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modificar los permisos de ubicación geográfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizar esto para permitir el envío de información de ubicación a sitios web arbitrarios."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7c6c1594124e..b75644b469e5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2053,7 +2053,7 @@
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Bližnjice ni bilo mogoče obnoviti zaradi neujemanja podpisa aplikacije"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Bližnjice ni bilo mogoče obnoviti"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Bližnjica je onemogočena"</string>
- <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ODSTRANI"</string>
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ODMESTI"</string>
<string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"VSEENO ODPRI"</string>
<string name="harmful_app_warning_title" msgid="8982527462829423432">"Zaznana je bila škodljiva aplikacija"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati izreze aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 726e223bbc78..7028672bd5eb 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -365,9 +365,9 @@
<string name="permlab_enableCarMode" msgid="5684504058192921098">"கார் பயன்முறையை இயக்குதல்"</string>
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"கார் முறையை இயக்க, ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"பிற பயன்பாடுகளை மூடுதல்"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"பிற பயன்பாடுகளின் பின்புலச் செயல்முறைகளை நிறுத்த ஆப்ஸை அனுமதிக்கிறது. இதனால் பிற பயன்பாடுகள் இயங்குவதை நிறுத்தலாம்."</string>
- <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"இந்த ஆப்ஸ் பிற பயன்பாடுகளின் மேலே தோன்றலாம்"</string>
- <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"இந்த ஆப்ஸ் பிற பயன்பாடுகளின் மேலே அல்லது திரையின் பிற பகுதிகளில் தோன்றலாம். இது வழக்கமான ஆப்ஸ் உபயோகத்தில் குறுக்கிட்டு, பிற பயன்பாடுகள் தோன்றும் விதத்தை மாற்றக்கூடும்."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"பிற ஆப்ஸின் பின்புலச் செயல்முறைகளை நிறுத்த ஆப்ஸை அனுமதிக்கிறது. இதனால் பிற பயன்பாடுகள் இயங்குவதை நிறுத்தலாம்."</string>
+ <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"இந்த ஆப்ஸ் பிற ஆப்ஸின் மேலே தோன்றலாம்"</string>
+ <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"இந்த ஆப்ஸ் பிற ஆப்ஸின் மேலே அல்லது திரையின் பிற பகுதிகளில் தோன்றலாம். இது வழக்கமான ஆப்ஸ் உபயோகத்தில் குறுக்கிட்டு, பிற பயன்பாடுகள் தோன்றும் விதத்தை மாற்றக்கூடும்."</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"பின்னணியில் இயக்கு"</string>
<string name="permdesc_runInBackground" msgid="7370142232209999824">"இந்த ஆப்ஸ், பின்னணியில் இயங்கலாம். இதனால் பேட்டரி விரைவாகத் தீர்ந்துவிடக்கூடும்."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"பின்னணியில் தரவைப் பயன்படுத்து"</string>
@@ -1380,7 +1380,7 @@
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"பிற ஆப்ஸின் மேலே காட்டு"</string>
- <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> பிற பயன்பாடுகளின் மீது தோன்றுகிறது"</string>
+ <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> பிற ஆப்ஸின் மீது தோன்றுகிறது"</string>
<string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> பிற ஆப்ஸின் மீது தோன்றுகிறது"</string>
<string name="alert_windows_notification_message" msgid="8917232109522912560">"<xliff:g id="NAME">%s</xliff:g> இந்த அம்சத்தைப் பயன்படுத்த வேண்டாம் என நினைத்தால், அமைப்புகளைத் திறந்து அதை முடக்க, தட்டவும்."</string>
<string name="alert_windows_notification_turn_off_action" msgid="2902891971380544651">"ஆஃப் செய்"</string>
@@ -2004,7 +2004,7 @@
<string name="standby_warning_message" product="default" msgid="5222741828239073484">"இந்தச் சாதனம் விரைவில் ஆஃப் ஆகலாம். இதைத் தொடர்ந்து ஆனில் வைக்கத் தட்டவும்."</string>
<string name="notification_appops_camera_active" msgid="5050283058419699771">"கேமரா"</string>
<string name="notification_appops_microphone_active" msgid="4335305527588191730">"மைக்ரோஃபோன்"</string>
- <string name="notification_appops_overlay_active" msgid="633813008357934729">"உங்கள் திரையில் உள்ள பிற பயன்பாடுகளின் மேல் காட்டுகிறது"</string>
+ <string name="notification_appops_overlay_active" msgid="633813008357934729">"உங்கள் திரையில் உள்ள பிற ஆப்ஸின் மேல் காட்டுகிறது"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"வழக்கமான பேட்டரி சேமிப்பானுக்கான விவர அறிவிப்பு"</string>
<string name="dynamic_mode_notification_title" msgid="508815255807182035">"வழக்கமாகச் சார்ஜ் செய்வதற்கு முன்பே பேட்டரி தீர்ந்துபோகக்கூடும்"</string>
<string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"பேட்டரி நிலையை நீட்டிக்க பேட்டரி சேமிப்பான் இயக்கப்பட்டுள்ளது"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d17093211040..3472cb2a0e9e 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -299,7 +299,7 @@
<string name="permgrouplab_microphone" msgid="171539900250043464">"ไมโครโฟน"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"บันทึกเสียง"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงไหม"</string>
- <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"กิจกรรมการเคลื่อนไหวร่างกาย"</string>
+ <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"การเคลื่อนไหวร่างกาย"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"เข้าถึงกิจกรรมการเคลื่อนไหวร่างกายของคุณ"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงกิจกรรมการเคลื่อนไหวร่างกายของคุณไหม"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"กล้องถ่ายรูป"</string>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index a92c50059a2a..f71c8b0558cf 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -70,7 +70,7 @@
<shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" />
<!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
- <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075" />
+ <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
<!-- Chile: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="cl" pattern="\\d{4,5}" free="9963|9240" />
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 2fc3e36e7948..4a93f42a0f7c 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -16,16 +16,29 @@
package android.content.res;
+import android.content.Context;
+import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
+import android.util.AtomicFile;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.usage.IntervalStatsProto;
+
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Locale;
+
/**
* Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest
*/
@@ -54,4 +67,70 @@ public class ConfigurationTest extends TestCase {
config2.updateFrom(config);
assertEquals(config2.screenLayout, Configuration.SCREENLAYOUT_COMPAT_NEEDED);
}
+
+ @Test
+ public void testReadWriteProto() throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final File testDir = new File(context.getFilesDir(), "ConfigurationTest");
+ testDir.mkdirs();
+ final File proto = new File(testDir, "configs");
+ if (proto.exists()) {
+ proto.delete();
+ }
+
+ final Locale arabic = new Locale.Builder().setLocale(new Locale("ar", "AE")).build();
+ final Locale urdu = new Locale.Builder().setLocale(new Locale("ur", "IN")).build();
+ final Locale urduExtension = new Locale.Builder().setLocale(new Locale("ur", "IN"))
+ .setExtension('u', "nu-latn").build();
+ Configuration write = new Configuration();
+ write.setLocales(new LocaleList(arabic, urdu, urduExtension));
+ writeToProto(proto, write);
+ assertTrue("Failed to write configs to proto.", proto.exists());
+
+ final Configuration read = new Configuration();
+ try {
+ readFromProto(proto, read);
+ } finally {
+ proto.delete();
+ }
+
+ assertEquals("Missing locales in proto file written to disk.",
+ read.getLocales().size(), write.getLocales().size());
+ assertTrue("Arabic locale not found in Configuration locale list.",
+ read.getLocales().indexOf(arabic) != -1);
+ assertTrue("Urdu locale not found in Configuration locale list.",
+ read.getLocales().indexOf(urdu) != -1);
+ assertTrue("Urdu locale with extensions not found in Configuration locale list.",
+ read.getLocales().indexOf(urduExtension) != -1);
+ }
+
+ private void writeToProto(File f, Configuration config) throws Exception {
+ final AtomicFile af = new AtomicFile(f);
+ FileOutputStream fos = af.startWrite();
+ try {
+ final ProtoOutputStream protoOut = new ProtoOutputStream(fos);
+ final long token = protoOut.start(IntervalStatsProto.CONFIGURATIONS);
+ config.writeToProto(protoOut, IntervalStatsProto.Configuration.CONFIG, false, false);
+ protoOut.end(token);
+ protoOut.flush();
+ af.finishWrite(fos);
+ fos = null;
+ } finally {
+ af.failWrite(fos);
+ }
+ }
+
+ private void readFromProto(File f, Configuration config) throws Exception {
+ final AtomicFile afRead = new AtomicFile(f);
+ try (FileInputStream in = afRead.openRead()) {
+ final ProtoInputStream protoIn = new ProtoInputStream(in);
+ if (protoIn.nextField(IntervalStatsProto.CONFIGURATIONS)) {
+ final long token = protoIn.start(IntervalStatsProto.CONFIGURATIONS);
+ if (protoIn.nextField(IntervalStatsProto.Configuration.CONFIG)) {
+ config.readFromProto(protoIn, IntervalStatsProto.Configuration.CONFIG);
+ protoIn.end(token);
+ }
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 4c59207b2d61..1bd52af09a4d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 34;
+ private static final int NUM_MARSHALLED_PROPERTIES = 35;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
index e1ccd7523eba..8891d3fd2dca 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
@@ -65,8 +65,10 @@ public class LegacyIntentClassificationFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
InstrumentationRegistry.getContext(),
@@ -101,8 +103,10 @@ public class LegacyIntentClassificationFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
InstrumentationRegistry.getContext(),
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
index b9a1a8cc4e42..bcea5fea6a13 100644
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
@@ -83,9 +83,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
createRemoteActionTemplates(),
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
@@ -124,9 +126,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
createRemoteActionTemplates(),
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
List<LabeledIntent> intents =
mTemplateClassificationIntentFactory.create(
@@ -162,8 +166,10 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
- 0,
- 0);
+ null,
+ 0L,
+ 0L,
+ 0d);
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
@@ -196,9 +202,11 @@ public class TemplateClassificationIntentFactoryTest {
null,
null,
null,
+ null,
new RemoteActionTemplate[0],
- 0,
- 0);
+ 0L,
+ 0L,
+ 0d);
mTemplateClassificationIntentFactory.create(
InstrumentationRegistry.getContext(),
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 767ec0e38a86..c44b7d81868d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -24,12 +24,19 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ChooserActivity.CALLER_TARGET_SCORE_BOOST;
+import static com.android.internal.app.ChooserActivity.SHORTCUT_TARGET_SCORE_BOOST;
+import static com.android.internal.app.ChooserActivity.TARGET_TYPE_CHOOSER_TARGET;
+import static com.android.internal.app.ChooserActivity.TARGET_TYPE_DEFAULT;
+import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
+import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -45,6 +52,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -770,6 +779,139 @@ public class ChooserActivityTest {
onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed()));
}
+ @Test
+ public void testGetBaseScore() {
+ final float testBaseScore = 0.89f;
+
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(sOverrides.resolverListController.getScore(Mockito.isA(
+ ResolverActivity.DisplayResolveInfo.class))).thenReturn(testBaseScore);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ final ResolverActivity.DisplayResolveInfo testDri =
+ activity.createTestDisplayResolveInfo(sendIntent,
+ ResolverDataProvider.createResolveInfo(3, 0), "testLabel", "testInfo", sendIntent);
+ final ChooserActivity.ChooserListAdapter adapter = activity.getAdapter();
+
+ assertThat(adapter.getBaseScore(null, 0), is(CALLER_TARGET_SCORE_BOOST));
+ assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore));
+ assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore));
+ assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE),
+ is(SHORTCUT_TARGET_SCORE_BOOST));
+ assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER),
+ is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
+ }
+
+ /**
+ * The case when AppPrediction service is not defined in PackageManager is already covered
+ * as a test parameter {@link ChooserActivityTest#packageManagers}. This test is checking the
+ * case when the prediction service is defined but the component is not available on the device.
+ */
+ @Test
+ public void testIsAppPredictionServiceAvailable() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ if (activity.getPackageManager().getAppPredictionServicePackageName() == null) {
+ assertThat(activity.isAppPredictionServiceAvailable(), is(false));
+ } else {
+ assertThat(activity.isAppPredictionServiceAvailable(), is(true));
+
+ sOverrides.resources = Mockito.spy(activity.getResources());
+ when(sOverrides.resources.getString(R.string.config_defaultAppPredictionService))
+ .thenReturn("ComponentNameThatDoesNotExist");
+
+ assertThat(activity.isAppPredictionServiceAvailable(), is(false));
+ }
+ }
+
+ @Test
+ public void testConvertToChooserTarget_predictionService() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
+
+ int[] expectedOrderAllShortcuts = {0, 1, 2, 3};
+ float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.98f, 0.97f};
+
+ List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
+ null, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderAllShortcuts, expectedScoreAllShortcuts);
+
+ List<ShareShortcutInfo> subset = new ArrayList<>();
+ subset.add(shortcuts.get(1));
+ subset.add(shortcuts.get(2));
+ subset.add(shortcuts.get(3));
+
+ int[] expectedOrderSubset = {1, 2, 3};
+ float[] expectedScoreSubset = {0.99f, 0.98f, 0.97f};
+
+ chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderSubset, expectedScoreSubset);
+ }
+
+ @Test
+ public void testConvertToChooserTarget_shortcutManager() {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ List<ShareShortcutInfo> shortcuts = createShortcuts(activity);
+
+ int[] expectedOrderAllShortcuts = {2, 0, 3, 1};
+ float[] expectedScoreAllShortcuts = {1.0f, 0.99f, 0.99f, 0.98f};
+
+ List<ChooserTarget> chooserTargets = activity.convertToChooserTarget(shortcuts, shortcuts,
+ null, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderAllShortcuts, expectedScoreAllShortcuts);
+
+ List<ShareShortcutInfo> subset = new ArrayList<>();
+ subset.add(shortcuts.get(1));
+ subset.add(shortcuts.get(2));
+ subset.add(shortcuts.get(3));
+
+ int[] expectedOrderSubset = {2, 3, 1};
+ float[] expectedScoreSubset = {1.0f, 0.99f, 0.98f};
+
+ chooserTargets = activity.convertToChooserTarget(subset, shortcuts, null,
+ TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER);
+ assertCorrectShortcutToChooserTargetConversion(shortcuts, chooserTargets,
+ expectedOrderSubset, expectedScoreSubset);
+ }
+
// This test is too long and too slow and should not be taken as an example for future tests.
@Test
public void testDirectTargetSelectionLogging() throws InterruptedException {
@@ -800,7 +942,7 @@ public class ChooserActivityTest {
"testInfo",
sendIntent),
serviceTargets,
- false)
+ TARGET_TYPE_CHOOSER_TARGET)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -866,7 +1008,7 @@ public class ChooserActivityTest {
"testInfo",
sendIntent),
serviceTargets,
- false)
+ TARGET_TYPE_CHOOSER_TARGET)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -927,7 +1069,7 @@ public class ChooserActivityTest {
"testInfo",
sendIntent),
serviceTargets,
- false)
+ TARGET_TYPE_CHOOSER_TARGET)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -1066,4 +1208,43 @@ public class ChooserActivityTest {
return bitmap;
}
+
+ private List<ShareShortcutInfo> createShortcuts(Context context) {
+ Intent testIntent = new Intent("TestIntent");
+
+ List<ShareShortcutInfo> shortcuts = new ArrayList<>();
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut1")
+ .setIntent(testIntent).setShortLabel("label1").setRank(3).build(), // 0 2
+ new ComponentName("package1", "class1")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut2")
+ .setIntent(testIntent).setShortLabel("label2").setRank(7).build(), // 1 3
+ new ComponentName("package2", "class2")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut3")
+ .setIntent(testIntent).setShortLabel("label3").setRank(1).build(), // 2 0
+ new ComponentName("package3", "class3")));
+ shortcuts.add(new ShareShortcutInfo(
+ new ShortcutInfo.Builder(context, "shortcut4")
+ .setIntent(testIntent).setShortLabel("label4").setRank(3).build(), // 3 2
+ new ComponentName("package4", "class4")));
+
+ return shortcuts;
+ }
+
+ private void assertCorrectShortcutToChooserTargetConversion(List<ShareShortcutInfo> shortcuts,
+ List<ChooserTarget> chooserTargets, int[] expectedOrder, float[] expectedScores) {
+ assertEquals(expectedOrder.length, chooserTargets.size());
+ for (int i = 0; i < chooserTargets.size(); i++) {
+ ChooserTarget ct = chooserTargets.get(i);
+ ShortcutInfo si = shortcuts.get(expectedOrder[i]).getShortcutInfo();
+ ComponentName cn = shortcuts.get(expectedOrder[i]).getTargetComponent();
+
+ assertEquals(si.getId(), ct.getIntentExtras().getString(Intent.EXTRA_SHORTCUT_ID));
+ assertEquals(si.getShortLabel(), ct.getTitle());
+ assertThat(Math.abs(expectedScores[i] - ct.getScore()) < 0.000001, is(true));
+ assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString());
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 44e56eaf0593..1d567c73f376 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -85,6 +86,14 @@ public class ChooserWrapperActivity extends ChooserActivity {
}
@Override
+ public Resources getResources() {
+ if (sOverrides.resources != null) {
+ return sOverrides.resources;
+ }
+ return super.getResources();
+ }
+
+ @Override
protected Bitmap loadThumbnail(Uri uri, Size size) {
if (sOverrides.previewThumbnail != null) {
return sOverrides.previewThumbnail;
@@ -145,6 +154,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
public Bitmap previewThumbnail;
public MetricsLogger metricsLogger;
public int alternateProfileSetting;
+ public Resources resources;
public void reset() {
onSafelyStartCallback = null;
@@ -157,6 +167,7 @@ public class ChooserWrapperActivity extends ChooserActivity {
resolverListController = mock(ResolverListController.class);
metricsLogger = mock(MetricsLogger.class);
alternateProfileSetting = 0;
+ resources = null;
}
}
}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 0a729a98a6a8..2141b815fb3b 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -194,6 +194,16 @@ public class HdmiAudioSystemClientTest {
}
@Override
+ public void addHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ }
+
+ @Override
+ public void removeHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ }
+
+ @Override
public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
}
diff --git a/data/keyboards/Vendor_045e_Product_02dd.kl b/data/keyboards/Vendor_045e_Product_02dd.kl
new file mode 100644
index 000000000000..3975cec24fcb
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_02dd.kl
@@ -0,0 +1,57 @@
+# 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.
+
+#
+# XBox One Controller - Model 1697 - USB
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 530926bf8dd0..67c181b452bb 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -511,13 +511,13 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect&
}
void SkiaPipeline::dumpResourceCacheUsage() const {
- int resources, maxResources;
- size_t bytes, maxBytes;
+ int resources;
+ size_t bytes;
mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
- mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes);
+ size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit();
SkString log("Resource Cache Usage:\n");
- log.appendf("%8d items out of %d maximum items\n", resources, maxResources);
+ log.appendf("%8d items\n", resources);
log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes,
bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 5469a6810c87..fc268138e071 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -69,8 +69,7 @@ void CacheManager::reset(sk_sp<GrContext> context) {
if (context) {
mGrContext = std::move(context);
- mGrContext->getResourceCacheLimits(&mMaxResources, nullptr);
- mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
}
}
@@ -119,8 +118,8 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
// limits between the background and max amounts. This causes the unlocked resources
// that have persistent data to be purged in LRU order.
mGrContext->purgeUnlockedResources(true);
- mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes);
- mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
+ mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index ad251f2364fe..d7977cc4566d 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -71,7 +71,6 @@ private:
sk_sp<GrContext> mGrContext;
#endif
- int mMaxResources = 0;
const size_t mMaxResourceBytes;
const size_t mBackgroundResourceBytes;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0ad050d7625e..b5ef8f43dfb6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -484,7 +484,7 @@ void CanvasContext::draw() {
swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
if (didDraw) {
- nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime();
+ nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get());
if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
// Ignoring dequeue duration as it happened prior to frame render start
// and thus is not part of the frame.
@@ -493,9 +493,7 @@ void CanvasContext::draw() {
swap.dequeueDuration =
us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get()));
}
- int durationUs;
- mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
- swap.queueDuration = us2ns(durationUs);
+ swap.queueDuration = us2ns(ANativeWindow_getLastQueueDuration(mNativeSurface.get()));
} else {
swap.dequeueDuration = 0;
swap.queueDuration = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index a44b80457218..864780fb6ae8 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -308,4 +308,4 @@ int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
return result;
}
-}; // namespace android::uirenderer::renderthread \ No newline at end of file
+}; // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 7f1a0781dd87..0d251b1f10e7 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -39,8 +39,6 @@ public:
int query(int what, int* value) const { return mSurface->query(what, value); }
- nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); }
-
uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
int getAndClearError() {
@@ -105,4 +103,4 @@ private:
static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
};
-}; // namespace android::uirenderer::renderthread \ No newline at end of file
+}; // namespace android::uirenderer::renderthread
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 97bc4042b86f..d06ba12f5e2a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -107,10 +107,6 @@ interface ILocationManager
List<LocationRequest> getTestProviderCurrentRequests(String provider, String opPackageName);
LocationTime getGnssTimeMillis();
- // --- deprecated ---
- void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
- String opPackageName);
-
boolean sendExtraCommand(String provider, String command, inout Bundle extras);
// --- internal ---
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1cc246b51765..5be4770440dc 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1543,44 +1543,19 @@ public class LocationManager {
/**
* This method has no effect as provider status has been deprecated and is no longer supported.
*
- * @param provider the provider name
- * @param status the mock status
- * @param extras a Bundle containing mock extras
- * @param updateTime the mock update time
- *
- * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
- * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
- * allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
- *
* @deprecated This method has no effect.
*/
@Deprecated
public void setTestProviderStatus(
- @NonNull String provider, int status, @Nullable Bundle extras, long updateTime) {
- try {
- mService.setTestProviderStatus(provider, status, extras, updateTime,
- mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ @NonNull String provider, int status, @Nullable Bundle extras, long updateTime) {}
/**
* This method has no effect as provider status has been deprecated and is no longer supported.
*
- * @param provider the provider name
- * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
- * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
- * allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
- *
* @deprecated This method has no effect.
*/
@Deprecated
- public void clearTestProviderStatus(@NonNull String provider) {
- setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
- }
+ public void clearTestProviderStatus(@NonNull String provider) {}
/**
* Get the last list of {@link LocationRequest}s sent to the provider.
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 8ae972bde4f9..4246c6cd1004 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -37,10 +37,4 @@ interface ILocationProvider {
@UnsupportedAppUsage
oneway void sendExtraCommand(String command, in Bundle extras);
-
- // --- deprecated and will be removed the future ---
- @UnsupportedAppUsage
- int getStatus(out Bundle extras);
- @UnsupportedAppUsage
- long getStatusUpdateTime();
}
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 274e0e4c2b65..07bfe0242509 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -31,7 +31,9 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.location.nano.GnssLogsProto.GnssLog;
import com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
/**
* GnssMetrics: Is used for logging GNSS metrics
@@ -63,9 +65,16 @@ public class GnssMetrics {
/* The time since boot when logging started */
private String mLogStartInElapsedRealTime;
+ /** The number of hertz in one MHz */
+ private static final double HZ_PER_MHZ = 1e6;
+
/* GNSS power metrics */
private GnssPowerMetrics mGnssPowerMetrics;
+ /** Frequency range of GPS L5, Galileo E5a, QZSS J5 frequency band */
+ private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * HZ_PER_MHZ;
+ private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * HZ_PER_MHZ;
+
/* A boolean array indicating whether the constellation types have been used in fix. */
private boolean[] mConstellationTypes;
/** Location failure statistics */
@@ -76,6 +85,16 @@ public class GnssMetrics {
private Statistics mPositionAccuracyMeterStatistics;
/** Top 4 average CN0 statistics */
private Statistics mTopFourAverageCn0Statistics;
+ /** Top 4 average CN0 statistics L5 */
+ private Statistics mTopFourAverageCn0StatisticsL5;
+ /** Total number of sv status messages processed */
+ private int mNumSvStatus;
+ /** Total number of L5 sv status messages processed */
+ private int mNumL5SvStatus;
+ /** Total number of sv status messages processed, where sv is used in fix */
+ private int mNumSvStatusUsedInFix;
+ /** Total number of L5 sv status messages processed, where sv is used in fix */
+ private int mNumL5SvStatusUsedInFix;
public GnssMetrics(IBatteryStats stats) {
mGnssPowerMetrics = new GnssPowerMetrics(stats);
@@ -83,6 +102,11 @@ public class GnssMetrics {
mTimeToFirstFixSecStatistics = new Statistics();
mPositionAccuracyMeterStatistics = new Statistics();
mTopFourAverageCn0Statistics = new Statistics();
+ mTopFourAverageCn0StatisticsL5 = new Statistics();
+ mNumSvStatus = 0;
+ mNumL5SvStatus = 0;
+ mNumSvStatusUsedInFix = 0;
+ mNumL5SvStatusUsedInFix = 0;
reset();
}
@@ -127,8 +151,14 @@ public class GnssMetrics {
/**
* Logs CN0 when at least 4 SVs are available
+ *
+ * @param cn0s
+ * @param numSv
+ * @param svCarrierFreqs
*/
- public void logCn0(float[] cn0s, int numSv) {
+ public void logCn0(float[] cn0s, int numSv, float[] svCarrierFreqs) {
+ // Calculate L5 Cn0
+ logCn0L5(numSv, cn0s, svCarrierFreqs);
if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) {
if (numSv == 0) {
mGnssPowerMetrics.reportSignalQuality(null, 0);
@@ -150,6 +180,75 @@ public class GnssMetrics {
mTopFourAverageCn0Statistics.addItem(top4AvgCn0);
}
}
+ /* Helper function to check if a SV is L5 */
+ private static boolean isL5Sv(float carrierFreq) {
+ return (carrierFreq >= L5_CARRIER_FREQ_RANGE_LOW_HZ
+ && carrierFreq <= L5_CARRIER_FREQ_RANGE_HIGH_HZ);
+ }
+
+ /**
+ * Logs sv status data
+ *
+ * @param svCount
+ * @param svidWithFlags
+ * @param svCarrierFreqs
+ */
+ public void logSvStatus(int svCount, int[] svidWithFlags, float[] svCarrierFreqs) {
+ boolean isL5 = false;
+ // Calculate SvStatus Information
+ for (int i = 0; i < svCount; i++) {
+ if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0) {
+ mNumSvStatus++;
+ isL5 = isL5Sv(svCarrierFreqs[i]);
+ if (isL5) {
+ mNumL5SvStatus++;
+ }
+ if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+ mNumSvStatusUsedInFix++;
+ if (isL5) {
+ mNumL5SvStatusUsedInFix++;
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ /**
+ * Logs CN0 when at least 4 SVs are available L5 Only
+ *
+ * @param svCount
+ * @param cn0s
+ * @param svCarrierFreqs
+ */
+ private void logCn0L5(int svCount, float[] cn0s, float[] svCarrierFreqs) {
+ if (svCount == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < svCount
+ || svCarrierFreqs == null || svCarrierFreqs.length == 0
+ || svCarrierFreqs.length < svCount) {
+ return;
+ }
+ // Create array list of all L5 satellites in report.
+ ArrayList<Float> CnoL5Array = new ArrayList();
+ for (int i = 0; i < svCount; i++) {
+ if (isL5Sv(svCarrierFreqs[i])) {
+ CnoL5Array.add(cn0s[i]);
+ }
+ }
+ if (CnoL5Array.size() == 0 || CnoL5Array.size() < 4) {
+ return;
+ }
+ int numSvL5 = CnoL5Array.size();
+ Collections.sort(CnoL5Array);
+ if (CnoL5Array.get(numSvL5 - 4) > 0.0) {
+ double top4AvgCn0 = 0.0;
+ for (int i = numSvL5 - 4; i < numSvL5; i++) {
+ top4AvgCn0 += (double) CnoL5Array.get(i);
+ }
+ top4AvgCn0 /= 4;
+ mTopFourAverageCn0StatisticsL5.addItem(top4AvgCn0);
+ }
+ return;
+ }
/**
* Logs that a constellation type has been observed.
@@ -189,6 +288,24 @@ public class GnssMetrics {
msg.standardDeviationTopFourAverageCn0DbHz =
mTopFourAverageCn0Statistics.getStandardDeviation();
}
+ if (mNumSvStatus > 0) {
+ msg.numSvStatusProcessed = mNumSvStatus;
+ }
+ if (mNumL5SvStatus > 0) {
+ msg.numL5SvStatusProcessed = mNumL5SvStatus;
+ }
+ if (mNumSvStatusUsedInFix > 0) {
+ msg.numSvStatusUsedInFix = mNumSvStatusUsedInFix;
+ }
+ if (mNumL5SvStatusUsedInFix > 0) {
+ msg.numL5SvStatusUsedInFix = mNumL5SvStatusUsedInFix;
+ }
+ if (mTopFourAverageCn0StatisticsL5.getCount() > 0) {
+ msg.numL5TopFourAverageCn0Processed = mTopFourAverageCn0StatisticsL5.getCount();
+ msg.meanL5TopFourAverageCn0DbHz = mTopFourAverageCn0StatisticsL5.getMean();
+ msg.standardDeviationL5TopFourAverageCn0DbHz =
+ mTopFourAverageCn0StatisticsL5.getStandardDeviation();
+ }
msg.powerMetrics = mGnssPowerMetrics.buildProto();
msg.hardwareRevision = SystemProperties.get("ro.boot.revision", "");
String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT);
@@ -238,6 +355,24 @@ public class GnssMetrics {
s.append(" Top 4 Avg CN0 standard deviation (dB-Hz): ").append(
mTopFourAverageCn0Statistics.getStandardDeviation()).append("\n");
}
+ s.append(" Total number of sv status messages processed: ").append(
+ mNumSvStatus).append("\n");
+ s.append(" Total number of L5 sv status messages processed: ").append(
+ mNumL5SvStatus).append("\n");
+ s.append(" Total number of sv status messages processed, "
+ + "where sv is used in fix: ").append(
+ mNumSvStatusUsedInFix).append("\n");
+ s.append(" Total number of L5 sv status messages processed, "
+ + "where sv is used in fix: ").append(
+ mNumL5SvStatusUsedInFix).append("\n");
+ s.append(" Number of L5 CN0 reports: ").append(
+ mTopFourAverageCn0StatisticsL5.getCount()).append("\n");
+ if (mTopFourAverageCn0StatisticsL5.getCount() > 0) {
+ s.append(" L5 Top 4 Avg CN0 mean (dB-Hz): ").append(
+ mTopFourAverageCn0StatisticsL5.getMean()).append("\n");
+ s.append(" L5 Top 4 Avg CN0 standard deviation (dB-Hz): ").append(
+ mTopFourAverageCn0StatisticsL5.getStandardDeviation()).append("\n");
+ }
s.append(" Used-in-fix constellation types: ");
for (int i = 0; i < mConstellationTypes.length; i++) {
if (mConstellationTypes[i]) {
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 6bde3a884c30..fc7bff3c6dab 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -256,17 +256,6 @@ public abstract class LocationProviderBase {
/**
* This method will no longer be invoked.
*
- * Returns a information on the status of this provider.
- * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
- * out of service, and this is not expected to change in the near
- * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if
- * the provider is temporarily unavailable but is expected to be
- * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned
- * if the provider is currently available.
- *
- * <p>If extras is non-null, additional status information may be
- * added to it in the form of provider-specific key/value pairs.
- *
* @deprecated This callback will be never be invoked on Android Q and above. This method should
* only be implemented in location providers that need to support SDKs below Android Q. This
* method may be removed in the future.
@@ -279,15 +268,6 @@ public abstract class LocationProviderBase {
/**
* This method will no longer be invoked.
*
- * Returns the time at which the status was last updated. It is the
- * responsibility of the provider to appropriately set this value using
- * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
- * there is a status update that it wishes to broadcast to all its
- * listeners. The provider should be careful not to broadcast
- * the same status again.
- *
- * @return time of last status update in millis since last reboot
- *
* @deprecated This callback will be never be invoked on Android Q and above. This method should
* only be implemented in location providers that need to support SDKs below Android Q. This
* method may be removed in the future.
@@ -332,16 +312,6 @@ public abstract class LocationProviderBase {
}
@Override
- public int getStatus(Bundle extras) {
- return onGetStatus(extras);
- }
-
- @Override
- public long getStatusUpdateTime() {
- return onGetStatusUpdateTime();
- }
-
- @Override
public void sendExtraCommand(String command, Bundle extras) {
onSendExtraCommand(command, extras);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e29e5698439d..7cd09de41346 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1184,8 +1184,15 @@ public class AudioTrack extends PlayerBase
int bufferSizeInBytes, int mode) {
// If no attributes, OK
// otherwise check attributes for USAGE_MEDIA and CONTENT_UNKNOWN, MUSIC, or MOVIE.
+ // Only consider flags that are not compatible with FLAG_DEEP_BUFFER. We include
+ // FLAG_DEEP_BUFFER because if set the request is explicit and
+ // shouldEnablePowerSaving() should return false.
+ final int flags = attributes.getAllFlags()
+ & (AudioAttributes.FLAG_DEEP_BUFFER | AudioAttributes.FLAG_LOW_LATENCY
+ | AudioAttributes.FLAG_HW_AV_SYNC | AudioAttributes.FLAG_BEACON);
+
if (attributes != null &&
- (attributes.getAllFlags() != 0 // cannot have any special flags
+ (flags != 0 // cannot have any special flags
|| attributes.getUsage() != AudioAttributes.USAGE_MEDIA
|| (attributes.getContentType() != AudioAttributes.CONTENT_TYPE_UNKNOWN
&& attributes.getContentType() != AudioAttributes.CONTENT_TYPE_MUSIC
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index c49b1e496c66..84fe27de15ab 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -52,7 +52,6 @@ cc_library_shared {
"libandroidfw",
"libhidlallocatorutils",
"libhidlbase",
- "libhidltransport",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
@@ -147,7 +146,6 @@ cc_library_shared {
"android.hidl.memory@1.0",
"libhidlbase",
"libhidlmemory",
- "libhidltransport",
"libbinderthreadstate",
// MediaPlayer2 implementation
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index 8c38d887f82b..9705b91dd52a 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -106,7 +106,8 @@ ssize_t JMediaDataSource::readAt(off64_t offset, size_t size) {
}
ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
- env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)mMemory->pointer());
+ env->GetByteArrayRegion(mByteArrayObj, 0, numread,
+ (jbyte*)mMemory->unsecurePointer());
return numread;
}
diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp
index aa79ce0a44ab..c61365a448d3 100644
--- a/media/jni/android_media_MediaDescrambler.cpp
+++ b/media/jni/android_media_MediaDescrambler.cpp
@@ -220,7 +220,7 @@ status_t JDescrambler::descramble(
return NO_MEMORY;
}
- memcpy(mMem->pointer(),
+ memcpy(mMem->unsecurePointer(),
(const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
DestinationBuffer dstBuffer;
@@ -248,7 +248,8 @@ status_t JDescrambler::descramble(
if (*status == Status::OK) {
if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) {
- memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *bytesWritten);
+ memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->unsecurePointer(),
+ *bytesWritten);
} else {
// status seems OK but bytesWritten is invalid, we really
// have no idea what is wrong.
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 365e045689f0..53adff3e251e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -148,7 +148,7 @@ static jint android_media_MediaHTTPConnection_native_readAt(
byteArrayObj,
0,
n,
- (jbyte *)conn->getIMemory()->pointer());
+ (jbyte *)conn->getIMemory()->unsecurePointer());
}
return n;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 3809bc4752a8..18fd1a01cfd6 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -396,8 +396,12 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
}
if (videoFrame == NULL) {
ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
@@ -423,7 +427,11 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
VideoFrame *videoFrame = NULL;
sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat);
if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
}
if (videoFrame == NULL) {
ALOGE("getImageAtIndex: videoFrame is a NULL pointer");
@@ -454,7 +462,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex(
sp<IMemory> frameMemory = retriever->getImageAtIndex(
index, colorFormat, true /*metaOnly*/, true /*thumbnail*/);
if (frameMemory != 0) {
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
int32_t thumbWidth = videoFrame->mWidth;
int32_t thumbHeight = videoFrame->mHeight;
videoFrame = NULL;
@@ -467,7 +479,11 @@ static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex(
|| thumbPixels * 6 >= maxPixels) {
frameMemory = retriever->getImageAtIndex(
index, colorFormat, false /*metaOnly*/, true /*thumbnail*/);
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ videoFrame = static_cast<VideoFrame *>(frameMemory->unsecurePointer());
if (thumbPixels > maxPixels) {
int downscale = ceil(sqrt(thumbPixels / (float)maxPixels));
@@ -514,11 +530,15 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtIndex(
size_t i = 0;
for (; i < numFrames; i++) {
sp<IMemory> frame = retriever->getFrameAtIndex(frameIndex + i, colorFormat);
- if (frame == NULL || frame->pointer() == NULL) {
+ if (frame == NULL || frame->unsecurePointer() == NULL) {
ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
break;
}
- VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame *videoFrame = static_cast<VideoFrame *>(frame->unsecurePointer());
jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj);
env->DeleteLocalRef(bitmapObj);
@@ -551,7 +571,11 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
// the method name to getEmbeddedPicture().
sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object
- mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->unsecurePointer());
}
if (mediaAlbumArt == NULL) {
ALOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 49066950a9fb..102bbf0e5931 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -951,6 +951,8 @@ void SoundChannel::process(int event, void *info, unsigned long toggle)
ALOGV("process %p channel %d event %s",
this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
"BUFFER_END");
+ // Only BUFFER_END should happen as we use static tracks.
+ setVolume_l(0.f, 0.f); // set volume to 0 to indicate no need to ramp volume down.
mSoundPool->addToStopList(this);
} else if (event == AudioTrack::EVENT_LOOP_END) {
ALOGV("End loop %p channel %d", this, mChannelID);
@@ -966,14 +968,18 @@ void SoundChannel::process(int event, void *info, unsigned long toggle)
bool SoundChannel::doStop_l()
{
if (mState != IDLE) {
- setVolume_l(0, 0);
ALOGV("stop");
- // Since we're forcibly halting the previously playing content,
- // we sleep here to ensure the volume is ramped down before we stop the track.
- // Ideally the sleep time is the mixer period, or an approximation thereof
- // (Fast vs Normal tracks are different).
- // TODO: consider pausing instead of stop here.
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ if (mLeftVolume != 0.f || mRightVolume != 0.f) {
+ setVolume_l(0.f, 0.f);
+ if (mSoundPool->attributes()->usage != AUDIO_USAGE_GAME) {
+ // Since we're forcibly halting the previously playing content,
+ // we sleep here to ensure the volume is ramped down before we stop the track.
+ // Ideally the sleep time is the mixer period, or an approximation thereof
+ // (Fast vs Normal tracks are different).
+ ALOGV("sleeping: ChannelID:%d SampleID:%d", mChannelID, mSample->sampleID());
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ }
+ }
mAudioTrack->stop();
mPrevSampleID = mSample->sampleID();
mSample.clear();
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 9d7410305c2c..01e4faae6f6c 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -61,7 +61,7 @@ public:
audio_channel_mask_t channelMask() { return mChannelMask; }
size_t size() { return mSize; }
int state() { return mState; }
- uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
+ uint8_t* data() { return static_cast<uint8_t*>(mData->unsecurePointer()); }
status_t doLoad();
void startLoad() { mState = LOADING; }
sp<IMemory> getIMemory() { return mData; }
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 58317edbea68..a0d20506613e 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -40,6 +40,8 @@ cc_library_shared {
"libandroid_runtime",
],
+ version_script: "libamidi.map.txt",
+
export_include_dirs: ["include"],
}
diff --git a/media/tests/audiotests/shared_mem_test.cpp b/media/tests/audiotests/shared_mem_test.cpp
index 2f5749933b54..d586b6a6da17 100644
--- a/media/tests/audiotests/shared_mem_test.cpp
+++ b/media/tests/audiotests/shared_mem_test.cpp
@@ -92,7 +92,7 @@ int AudioTrackTest::Test01() {
iMem = heap->allocate(BUF_SZ*sizeof(short));
- p = static_cast<uint8_t*>(iMem->pointer());
+ p = static_cast<uint8_t*>(iMem->unsecurePointer());
memcpy(p, smpBuf, BUF_SZ*sizeof(short));
sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
new file mode 100644
index 000000000000..033f1b10118c
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.KeyStoreException;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * State about encrypted backups that needs to be remembered.
+ */
+public class CryptoSettings {
+
+ private static final String TAG = "CryptoSettings";
+
+ private static final String SHARED_PREFERENCES_NAME = "crypto_settings";
+
+ private static final String KEY_IS_INITIALIZED = "isInitialized";
+ private static final String KEY_ACTIVE_SECONDARY_ALIAS = "activeSecondary";
+ private static final String KEY_NEXT_SECONDARY_ALIAS = "nextSecondary";
+ private static final String SECONDARY_KEY_LAST_ROTATED_AT = "secondaryKeyLastRotatedAt";
+ private static final String[] SETTINGS_FOR_BACKUP = {
+ KEY_IS_INITIALIZED,
+ KEY_ACTIVE_SECONDARY_ALIAS,
+ KEY_NEXT_SECONDARY_ALIAS,
+ SECONDARY_KEY_LAST_ROTATED_AT
+ };
+
+ private static final long DEFAULT_SECONDARY_KEY_ROTATION_PERIOD =
+ TimeUnit.MILLISECONDS.convert(31, TimeUnit.DAYS);
+
+ private static final String KEY_ANCESTRAL_SECONDARY_KEY_VERSION =
+ "ancestral_secondary_key_version";
+
+ private final SharedPreferences mSharedPreferences;
+ private final Context mContext;
+
+ /**
+ * A new instance.
+ *
+ * @param context For looking up the {@link SharedPreferences}, for storing state.
+ * @return The instance.
+ */
+ public static CryptoSettings getInstance(Context context) {
+ // We need single process mode because CryptoSettings may be used from several processes
+ // simultaneously.
+ SharedPreferences sharedPreferences =
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+ return new CryptoSettings(sharedPreferences, context);
+ }
+
+ /**
+ * A new instance using {@link SharedPreferences} in the default mode.
+ *
+ * <p>This will not work across multiple processes but will work in tests.
+ */
+ @VisibleForTesting
+ public static CryptoSettings getInstanceForTesting(Context context) {
+ SharedPreferences sharedPreferences =
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+ return new CryptoSettings(sharedPreferences, context);
+ }
+
+ private CryptoSettings(SharedPreferences sharedPreferences, Context context) {
+ mSharedPreferences = checkNotNull(sharedPreferences);
+ mContext = checkNotNull(context);
+ }
+
+ /**
+ * The alias of the current active secondary key. This should be used to retrieve the key from
+ * AndroidKeyStore.
+ */
+ public Optional<String> getActiveSecondaryKeyAlias() {
+ return getStringInSharedPrefs(KEY_ACTIVE_SECONDARY_ALIAS);
+ }
+
+ /**
+ * The alias of the secondary key to which the client is rotating. The rotation is not
+ * immediate, which is why this setting is needed. Once the next key is created, it can take up
+ * to 72 hours potentially (or longer if the user has no network) for the next key to be synced
+ * with the keystore. Only after that has happened does the client attempt to re-wrap all
+ * tertiary keys and commit the rotation.
+ */
+ public Optional<String> getNextSecondaryKeyAlias() {
+ return getStringInSharedPrefs(KEY_NEXT_SECONDARY_ALIAS);
+ }
+
+ /**
+ * If the settings have been initialized.
+ */
+ public boolean getIsInitialized() {
+ return mSharedPreferences.getBoolean(KEY_IS_INITIALIZED, false);
+ }
+
+ /**
+ * Sets the alias of the currently active secondary key.
+ *
+ * @param activeAlias The alias, as in AndroidKeyStore.
+ * @throws IllegalArgumentException if the alias is not in the user's keystore.
+ */
+ public void setActiveSecondaryKeyAlias(String activeAlias) throws IllegalArgumentException {
+ assertIsValidAlias(activeAlias);
+ mSharedPreferences.edit().putString(KEY_ACTIVE_SECONDARY_ALIAS, activeAlias).apply();
+ }
+
+ /**
+ * Sets the alias of the secondary key to which the client is rotating.
+ *
+ * @param nextAlias The alias, as in AndroidKeyStore.
+ * @throws KeyStoreException if unable to check whether alias is valid in the keystore.
+ * @throws IllegalArgumentException if the alias is not in the user's keystore.
+ */
+ public void setNextSecondaryAlias(String nextAlias) throws IllegalArgumentException {
+ assertIsValidAlias(nextAlias);
+ mSharedPreferences.edit().putString(KEY_NEXT_SECONDARY_ALIAS, nextAlias).apply();
+ }
+
+ /**
+ * Unsets the alias of the key to which the client is rotating. This is generally performed once
+ * a rotation is complete.
+ */
+ public void removeNextSecondaryKeyAlias() {
+ mSharedPreferences.edit().remove(KEY_NEXT_SECONDARY_ALIAS).apply();
+ }
+
+ /**
+ * Sets the timestamp of when the secondary key was last rotated.
+ *
+ * @param timestamp The timestamp to set.
+ */
+ public void setSecondaryLastRotated(long timestamp) {
+ mSharedPreferences.edit().putLong(SECONDARY_KEY_LAST_ROTATED_AT, timestamp).apply();
+ }
+
+ /**
+ * Returns a timestamp of when the secondary key was last rotated.
+ *
+ * @return The timestamp.
+ */
+ public Optional<Long> getSecondaryLastRotated() {
+ if (!mSharedPreferences.contains(SECONDARY_KEY_LAST_ROTATED_AT)) {
+ return Optional.empty();
+ }
+ return Optional.of(mSharedPreferences.getLong(SECONDARY_KEY_LAST_ROTATED_AT, -1));
+ }
+
+ /**
+ * Sets the settings to have been initialized. (Otherwise loading should try to initialize
+ * again.)
+ */
+ private void setIsInitialized() {
+ mSharedPreferences.edit().putBoolean(KEY_IS_INITIALIZED, true).apply();
+ }
+
+ /**
+ * Initializes with the given key alias.
+ *
+ * @param alias The secondary key alias to be set as active.
+ * @throws IllegalArgumentException if the alias does not reference a valid key.
+ * @throws IllegalStateException if attempting to initialize an already initialized settings.
+ */
+ public void initializeWithKeyAlias(String alias) throws IllegalArgumentException {
+ checkState(
+ !getIsInitialized(), "Attempting to initialize an already initialized settings.");
+ setActiveSecondaryKeyAlias(alias);
+ setIsInitialized();
+ }
+
+ /** Returns the secondary key version of the encrypted backup set to restore from (if set). */
+ public Optional<String> getAncestralSecondaryKeyVersion() {
+ return Optional.ofNullable(
+ mSharedPreferences.getString(KEY_ANCESTRAL_SECONDARY_KEY_VERSION, null));
+ }
+
+ /** Sets the secondary key version of the encrypted backup set to restore from. */
+ public void setAncestralSecondaryKeyVersion(String ancestralSecondaryKeyVersion) {
+ mSharedPreferences
+ .edit()
+ .putString(KEY_ANCESTRAL_SECONDARY_KEY_VERSION, ancestralSecondaryKeyVersion)
+ .apply();
+ }
+
+ /** The number of milliseconds between secondary key rotation */
+ public long backupSecondaryKeyRotationIntervalMs() {
+ return DEFAULT_SECONDARY_KEY_ROTATION_PERIOD;
+ }
+
+ /** Deletes all crypto settings related to backup (as opposed to restore). */
+ public void clearAllSettingsForBackup() {
+ Editor sharedPrefsEditor = mSharedPreferences.edit();
+ for (String backupSettingKey : SETTINGS_FOR_BACKUP) {
+ sharedPrefsEditor.remove(backupSettingKey);
+ }
+ sharedPrefsEditor.apply();
+
+ Slog.d(TAG, "Cleared crypto settings for backup");
+ }
+
+ /**
+ * Throws {@link IllegalArgumentException} if the alias does not refer to a key that is in
+ * the {@link RecoveryController}.
+ */
+ private void assertIsValidAlias(String alias) throws IllegalArgumentException {
+ try {
+ if (!RecoveryController.getInstance(mContext).getAliases().contains(alias)) {
+ throw new IllegalArgumentException(alias + " is not in RecoveryController");
+ }
+ } catch (InternalRecoveryServiceException e) {
+ throw new IllegalArgumentException("Problem accessing recovery service", e);
+ }
+ }
+
+ private Optional<String> getStringInSharedPrefs(String key) {
+ return Optional.ofNullable(mSharedPreferences.getString(key, null));
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java
new file mode 100644
index 000000000000..6fb958bd1c1e
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RestoreKeyFetcher.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager.RecoverableKeyStoreSecondaryKeyManagerProvider;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/** Fetches the secondary key and uses it to unwrap the tertiary key during restore. */
+public class RestoreKeyFetcher {
+
+ /**
+ * Retrieves the secondary key with the given alias and uses it to unwrap the given wrapped
+ * tertiary key.
+ *
+ * @param secondaryKeyManagerProvider Provider which creates {@link
+ * RecoverableKeyStoreSecondaryKeyManager}
+ * @param secondaryKeyAlias Alias of the secondary key used to wrap the tertiary key
+ * @param wrappedTertiaryKey Tertiary key wrapped with the secondary key above
+ * @return The unwrapped tertiary key
+ */
+ public static SecretKey unwrapTertiaryKey(
+ RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
+ String secondaryKeyAlias,
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey)
+ throws KeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ NoSuchPaddingException {
+ Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
+ getSecondaryKey(secondaryKeyManagerProvider, secondaryKeyAlias);
+ if (!secondaryKey.isPresent()) {
+ throw new KeyException("No key:" + secondaryKeyAlias);
+ }
+
+ return KeyWrapUtils.unwrap(secondaryKey.get().getSecretKey(), wrappedTertiaryKey);
+ }
+
+ private static Optional<RecoverableKeyStoreSecondaryKey> getSecondaryKey(
+ RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
+ String secondaryKeyAlias)
+ throws KeyException {
+ try {
+ return secondaryKeyManagerProvider.get().get(secondaryKeyAlias);
+ } catch (InternalRecoveryServiceException | UnrecoverableKeyException e) {
+ throw new KeyException("Could not retrieve key:" + secondaryKeyAlias, e);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
new file mode 100644
index 000000000000..91b57cf69795
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationScheduler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
+
+import java.io.File;
+import java.time.Clock;
+import java.util.Optional;
+
+/**
+ * Helps schedule rotations of secondary keys.
+ *
+ * <p>TODO(b/72028016) Replace with a job.
+ */
+public class SecondaryKeyRotationScheduler {
+
+ private static final String TAG = "SecondaryKeyRotationScheduler";
+ private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
+
+ private final Context mContext;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private final CryptoSettings mCryptoSettings;
+ private final Clock mClock;
+
+ public SecondaryKeyRotationScheduler(
+ Context context,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
+ CryptoSettings cryptoSettings,
+ Clock clock) {
+ mContext = context;
+ mCryptoSettings = cryptoSettings;
+ mClock = clock;
+ mSecondaryKeyManager = secondaryKeyManager;
+ }
+
+ /**
+ * Returns {@code true} if a sentinel file for forcing secondary key rotation is present. This
+ * is only for testing purposes.
+ */
+ private boolean isForceRotationTestSentinelPresent() {
+ File file = new File(mContext.getFilesDir(), SENTINEL_FILE_PATH);
+ if (file.exists()) {
+ file.delete();
+ return true;
+ }
+ return false;
+ }
+
+ /** Start the key rotation task if it's time to do so */
+ public void startRotationIfScheduled() {
+ if (isForceRotationTestSentinelPresent()) {
+ Slog.i(TAG, "Found force flag for secondary rotation. Starting now.");
+ startRotation();
+ return;
+ }
+
+ Optional<Long> maybeLastRotated = mCryptoSettings.getSecondaryLastRotated();
+ if (!maybeLastRotated.isPresent()) {
+ Slog.v(TAG, "No previous rotation, scheduling from now.");
+ scheduleRotationFromNow();
+ return;
+ }
+
+ long lastRotated = maybeLastRotated.get();
+ long now = mClock.millis();
+
+ if (lastRotated > now) {
+ Slog.i(TAG, "Last rotation was in the future. Clock must have changed. Rotate now.");
+ startRotation();
+ return;
+ }
+
+ long millisSinceLastRotation = now - lastRotated;
+ long rotationInterval = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
+ if (millisSinceLastRotation >= rotationInterval) {
+ Slog.i(
+ TAG,
+ "Last rotation was more than "
+ + rotationInterval
+ + "ms ("
+ + millisSinceLastRotation
+ + "ms) in the past. Rotate now.");
+ startRotation();
+ }
+
+ Slog.v(TAG, "No rotation required, last " + lastRotated + ".");
+ }
+
+ private void startRotation() {
+ scheduleRotationFromNow();
+ new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager).run();
+ }
+
+ private void scheduleRotationFromNow() {
+ mCryptoSettings.setSecondaryLastRotated(mClock.millis());
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java
new file mode 100644
index 000000000000..a78357984912
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyManager.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Optional;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Gets the correct tertiary key to use during a backup, rotating it if required.
+ *
+ * <p>Calling any method on this class will count a incremental backup against the app, and the key
+ * will be rotated if required.
+ */
+public class TertiaryKeyManager {
+
+ private static final String TAG = "TertiaryKeyMgr";
+
+ private final TertiaryKeyStore mKeyStore;
+ private final TertiaryKeyGenerator mKeyGenerator;
+ private final TertiaryKeyRotationScheduler mTertiaryKeyRotationScheduler;
+ private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private final String mPackageName;
+
+ private boolean mKeyRotated;
+ @Nullable private SecretKey mTertiaryKey;
+
+ public TertiaryKeyManager(
+ Context context,
+ SecureRandom secureRandom,
+ TertiaryKeyRotationScheduler tertiaryKeyRotationScheduler,
+ RecoverableKeyStoreSecondaryKey secondaryKey,
+ String packageName) {
+ mSecondaryKey = secondaryKey;
+ mPackageName = packageName;
+ mKeyGenerator = new TertiaryKeyGenerator(secureRandom);
+ mKeyStore = TertiaryKeyStore.newInstance(context, secondaryKey);
+ mTertiaryKeyRotationScheduler = tertiaryKeyRotationScheduler;
+ }
+
+ /**
+ * Returns either the previously used tertiary key, or a new tertiary key if there was no
+ * previous key or it needed to be rotated.
+ */
+ public SecretKey getKey()
+ throws InvalidKeyException, IOException, IllegalBlockSizeException,
+ NoSuchPaddingException, NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+ init();
+ return mTertiaryKey;
+ }
+
+ /** Returns the key given by {@link #getKey()} wrapped by the secondary key. */
+ public WrappedKeyProto.WrappedKey getWrappedKey()
+ throws InvalidKeyException, IOException, IllegalBlockSizeException,
+ NoSuchPaddingException, NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+ init();
+ return KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), mTertiaryKey);
+ }
+
+ /**
+ * Returns {@code true} if a new tertiary key was generated at the start of this session,
+ * otherwise {@code false}.
+ */
+ public boolean wasKeyRotated()
+ throws InvalidKeyException, IllegalBlockSizeException, IOException,
+ NoSuchPaddingException, NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+ init();
+ return mKeyRotated;
+ }
+
+ private void init()
+ throws IllegalBlockSizeException, InvalidKeyException, IOException,
+ NoSuchAlgorithmException, NoSuchPaddingException,
+ InvalidAlgorithmParameterException {
+ if (mTertiaryKey != null) {
+ return;
+ }
+
+ Optional<SecretKey> key = getExistingKeyIfNotRotated();
+
+ if (!key.isPresent()) {
+ Slog.d(TAG, "Generating new tertiary key for " + mPackageName);
+
+ key = Optional.of(mKeyGenerator.generate());
+ mKeyRotated = true;
+ mTertiaryKeyRotationScheduler.recordKeyRotation(mPackageName);
+ mKeyStore.save(mPackageName, key.get());
+ }
+
+ mTertiaryKey = key.get();
+
+ mTertiaryKeyRotationScheduler.recordBackup(mPackageName);
+ }
+
+ private Optional<SecretKey> getExistingKeyIfNotRotated()
+ throws InvalidKeyException, IOException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchPaddingException {
+ if (mTertiaryKeyRotationScheduler.isKeyRotationDue(mPackageName)) {
+ Slog.i(TAG, "Tertiary key rotation was required for " + mPackageName);
+ return Optional.empty();
+ } else {
+ Slog.i(TAG, "Tertiary key rotation was not required");
+ return mKeyStore.load(mPackageName);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java
new file mode 100644
index 000000000000..f16a68d64213
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationScheduler.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Schedules tertiary key rotations in a staggered fashion.
+ *
+ * <p>Apps are due a key rotation after a certain number of backups. Rotations are then staggerered
+ * over a period of time, through restricting the number of rotations allowed in a 24-hour window.
+ * This will causes the apps to enter a staggered cycle of regular rotations.
+ *
+ * <p>Note: the methods in this class are not optimized to be super fast. They make blocking IO to
+ * ensure that scheduler information is committed to disk, so that it is available after the user
+ * turns their device off and on. This ought to be fine as
+ *
+ * <ul>
+ * <li>It will be invoked before a backup, so should never be invoked on the UI thread
+ * <li>It will be invoked before a backup, so the vast amount of time is spent on the backup, not
+ * writing tiny amounts of data to disk.
+ * </ul>
+ */
+public class TertiaryKeyRotationScheduler {
+ /** Default number of key rotations allowed within 24 hours. */
+ private static final int KEY_ROTATION_LIMIT = 2;
+
+ /** A new instance, using {@code context} to determine where to store state. */
+ public static TertiaryKeyRotationScheduler getInstance(Context context) {
+ TertiaryKeyRotationWindowedCount windowedCount =
+ TertiaryKeyRotationWindowedCount.getInstance(context);
+ TertiaryKeyRotationTracker tracker = TertiaryKeyRotationTracker.getInstance(context);
+ return new TertiaryKeyRotationScheduler(tracker, windowedCount, KEY_ROTATION_LIMIT);
+ }
+
+ private final TertiaryKeyRotationTracker mTracker;
+ private final TertiaryKeyRotationWindowedCount mWindowedCount;
+ private final int mMaximumRotationsPerWindow;
+
+ /**
+ * A new instance.
+ *
+ * @param tracker Tracks how many times each application has backed up.
+ * @param windowedCount Tracks how many rotations have happened in the last 24 hours.
+ * @param maximumRotationsPerWindow The maximum number of key rotations allowed per 24 hours.
+ */
+ @VisibleForTesting
+ TertiaryKeyRotationScheduler(
+ TertiaryKeyRotationTracker tracker,
+ TertiaryKeyRotationWindowedCount windowedCount,
+ int maximumRotationsPerWindow) {
+ mTracker = tracker;
+ mWindowedCount = windowedCount;
+ mMaximumRotationsPerWindow = maximumRotationsPerWindow;
+ }
+
+ /**
+ * Returns {@code true} if the app with {@code packageName} is due having its key rotated.
+ *
+ * <p>This ought to be queried before backing up an app, to determine whether to do an
+ * incremental backup or a full backup. (A full backup forces key rotation.)
+ */
+ public boolean isKeyRotationDue(String packageName) {
+ if (mWindowedCount.getCount() >= mMaximumRotationsPerWindow) {
+ return false;
+ }
+ return mTracker.isKeyRotationDue(packageName);
+ }
+
+ /**
+ * Records that a backup happened for the app with the given {@code packageName}.
+ *
+ * <p>Each backup brings the app closer to the point at which a key rotation is due.
+ */
+ public void recordBackup(String packageName) {
+ mTracker.recordBackup(packageName);
+ }
+
+ /**
+ * Records a key rotation happened for the app with the given {@code packageName}.
+ *
+ * <p>This resets the countdown until the next key rotation is due.
+ */
+ public void recordKeyRotation(String packageName) {
+ mTracker.resetCountdown(packageName);
+ mWindowedCount.record();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
index ec90f6c8c95e..1a281e79cc48 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,6 +16,8 @@
package com.android.server.backup.encryption.keys;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Slog;
@@ -46,15 +48,27 @@ public class TertiaryKeyRotationTracker {
*/
public static TertiaryKeyRotationTracker getInstance(Context context) {
return new TertiaryKeyRotationTracker(
- context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE));
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE),
+ MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
}
private final SharedPreferences mSharedPreferences;
+ private final int mMaxBackupsTillRotation;
- /** New instance, storing data in {@code mSharedPreferences}. */
+ /**
+ * New instance, storing data in {@code sharedPreferences} and initializing backup countdown to
+ * {@code maxBackupsTillRotation}.
+ */
@VisibleForTesting
- TertiaryKeyRotationTracker(SharedPreferences sharedPreferences) {
+ TertiaryKeyRotationTracker(SharedPreferences sharedPreferences, int maxBackupsTillRotation) {
+ checkArgument(
+ maxBackupsTillRotation >= 0,
+ String.format(
+ Locale.US,
+ "maxBackupsTillRotation should be non-negative but was %d",
+ maxBackupsTillRotation));
mSharedPreferences = sharedPreferences;
+ mMaxBackupsTillRotation = maxBackupsTillRotation;
}
/**
@@ -63,7 +77,7 @@ public class TertiaryKeyRotationTracker {
* @param packageName The package name of the app.
*/
public boolean isKeyRotationDue(String packageName) {
- return getBackupsSinceRotation(packageName) >= MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION;
+ return getBackupsSinceRotation(packageName) >= mMaxBackupsTillRotation;
}
/**
@@ -84,7 +98,7 @@ public class TertiaryKeyRotationTracker {
packageName,
Math.max(
0,
- MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION
+ mMaxBackupsTillRotation
- backupsSinceRotation)));
}
}
@@ -102,7 +116,7 @@ public class TertiaryKeyRotationTracker {
public void markAllForRotation() {
SharedPreferences.Editor editor = mSharedPreferences.edit();
for (String packageName : mSharedPreferences.getAll().keySet()) {
- editor.putInt(packageName, MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
+ editor.putInt(packageName, mMaxBackupsTillRotation);
}
editor.apply();
}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java
new file mode 100644
index 000000000000..b90343ad4b35
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCount.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tracks (and commits to disk) how many key rotations have happened in the last 24 hours. This
+ * allows us to limit (and therefore stagger) the number of key rotations in a given period of time.
+ *
+ * <p>Note to engineers thinking of replacing the below with fancier algorithms and data structures:
+ * we expect the total size of this count at any time to be below however many rotations we allow in
+ * the window, which is going to be in single digits. Any changes that mean we write to disk more
+ * frequently, that the code is no longer resistant to clock changes, or that the code is more
+ * difficult to understand are almost certainly not worthwhile.
+ */
+public class TertiaryKeyRotationWindowedCount {
+ private static final String TAG = "TertiaryKeyRotCount";
+
+ private static final int WINDOW_IN_HOURS = 24;
+ private static final String LOG_FILE_NAME = "tertiary_key_rotation_windowed_count";
+
+ private final Clock mClock;
+ private final File mFile;
+ private ArrayList<Long> mEvents;
+
+ /** Returns a new instance, persisting state to the files dir of {@code context}. */
+ public static TertiaryKeyRotationWindowedCount getInstance(Context context) {
+ File logFile = new File(context.getFilesDir(), LOG_FILE_NAME);
+ return new TertiaryKeyRotationWindowedCount(logFile, Clock.systemDefaultZone());
+ }
+
+ /** A new instance, committing state to {@code file}, and reading time from {@code clock}. */
+ @VisibleForTesting
+ TertiaryKeyRotationWindowedCount(File file, Clock clock) {
+ mFile = file;
+ mClock = clock;
+ mEvents = new ArrayList<>();
+ try {
+ loadFromFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + LOG_FILE_NAME, e);
+ }
+ }
+
+ /** Records a key rotation at the current time. */
+ public void record() {
+ mEvents.add(mClock.millis());
+ compact();
+ try {
+ saveToFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error saving " + LOG_FILE_NAME, e);
+ }
+ }
+
+ /** Returns the number of key rotation that have been recorded in the window. */
+ public int getCount() {
+ compact();
+ return mEvents.size();
+ }
+
+ private void compact() {
+ long minimumTimestamp = getMinimumTimestamp();
+ long now = mClock.millis();
+ ArrayList<Long> compacted = new ArrayList<>();
+ for (long event : mEvents) {
+ if (event >= minimumTimestamp && event <= now) {
+ compacted.add(event);
+ }
+ }
+ mEvents = compacted;
+ }
+
+ private long getMinimumTimestamp() {
+ return mClock.millis() - TimeUnit.HOURS.toMillis(WINDOW_IN_HOURS) + 1;
+ }
+
+ private void loadFromFile() throws IOException {
+ if (!mFile.exists()) {
+ return;
+ }
+ try (FileInputStream fis = new FileInputStream(mFile);
+ DataInputStream dis = new DataInputStream(fis)) {
+ while (true) {
+ mEvents.add(dis.readLong());
+ }
+ } catch (EOFException eof) {
+ // expected
+ }
+ }
+
+ private void saveToFile() throws IOException {
+ // File size is maximum number of key rotations in window multiplied by 8 bytes, which is
+ // why
+ // we just overwrite it each time. We expect it will always be less than 100 bytes in size.
+ try (FileOutputStream fos = new FileOutputStream(mFile);
+ DataOutputStream dos = new DataOutputStream(fos)) {
+ for (long event : mEvents) {
+ dos.writeLong(event);
+ }
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
new file mode 100644
index 000000000000..01444bf0cd00
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.TertiaryKey;
+import com.android.server.backup.encryption.storage.TertiaryKeysTable;
+
+import com.google.protobuf.nano.CodedOutputByteBufferNano;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Stores backup package keys. Each application package has its own {@link SecretKey}, which is used
+ * to encrypt the backup data. These keys are then wrapped by a master backup key, and stored in
+ * their wrapped form on disk and on the backup server.
+ *
+ * <p>For now this code only implements writing to disk. Once the backup server is ready, it will be
+ * extended to sync the keys there, also.
+ */
+public class TertiaryKeyStore {
+
+ private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private final BackupEncryptionDb mDatabase;
+
+ /**
+ * Creates an instance, using {@code secondaryKey} to wrap tertiary keys, and storing them in
+ * the database.
+ */
+ public static TertiaryKeyStore newInstance(
+ Context context, RecoverableKeyStoreSecondaryKey secondaryKey) {
+ return new TertiaryKeyStore(secondaryKey, BackupEncryptionDb.newInstance(context));
+ }
+
+ private TertiaryKeyStore(
+ RecoverableKeyStoreSecondaryKey secondaryKey, BackupEncryptionDb database) {
+ mSecondaryKey = secondaryKey;
+ mDatabase = database;
+ }
+
+ /**
+ * Saves the given key.
+ *
+ * @param applicationName The package name of the application for which this key will be used to
+ * encrypt data. e.g., "com.example.app".
+ * @param key The key.
+ * @throws InvalidKeyException if the backup key is not capable of wrapping.
+ * @throws IOException if there is an issue writing to the database.
+ */
+ public void save(String applicationName, SecretKey key)
+ throws IOException, InvalidKeyException, IllegalBlockSizeException,
+ NoSuchPaddingException, NoSuchAlgorithmException {
+ checkApplicationName(applicationName);
+
+ byte[] keyBytes = getEncodedKey(KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), key));
+
+ long pk;
+ try {
+ pk =
+ mDatabase
+ .getTertiaryKeysTable()
+ .addKey(
+ new TertiaryKey(
+ mSecondaryKey.getAlias(), applicationName, keyBytes));
+ } finally {
+ mDatabase.close();
+ }
+
+ if (pk == -1) {
+ throw new IOException("Failed to commit to db");
+ }
+ }
+
+ /**
+ * Tries to load a key for the given application.
+ *
+ * @param applicationName The package name of the application, e.g. "com.example.app".
+ * @return The key if it is exists, {@link Optional#empty()} ()} otherwise.
+ * @throws InvalidKeyException if the backup key is not good for unwrapping.
+ * @throws IOException if there is a problem loading the key from the database.
+ */
+ public Optional<SecretKey> load(String applicationName)
+ throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchPaddingException {
+ checkApplicationName(applicationName);
+
+ Optional<TertiaryKey> keyFromDb;
+ try {
+ keyFromDb =
+ mDatabase
+ .getTertiaryKeysTable()
+ .getKey(mSecondaryKey.getAlias(), applicationName);
+ } finally {
+ mDatabase.close();
+ }
+
+ if (!keyFromDb.isPresent()) {
+ return Optional.empty();
+ }
+
+ WrappedKeyProto.WrappedKey wrappedKey =
+ WrappedKeyProto.WrappedKey.parseFrom(keyFromDb.get().getWrappedKeyBytes());
+ return Optional.of(KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
+ }
+
+ /**
+ * Loads keys for all applications.
+ *
+ * @return All of the keys in a map keyed by package name.
+ * @throws IOException if there is an issue loading from the database.
+ * @throws InvalidKeyException if the backup key is not an appropriate key for unwrapping.
+ */
+ public Map<String, SecretKey> getAll()
+ throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchPaddingException {
+ Map<String, TertiaryKey> tertiaries;
+ try {
+ tertiaries = mDatabase.getTertiaryKeysTable().getAllKeys(mSecondaryKey.getAlias());
+ } finally {
+ mDatabase.close();
+ }
+
+ Map<String, SecretKey> unwrappedKeys = new ArrayMap<>();
+ for (String applicationName : tertiaries.keySet()) {
+ WrappedKeyProto.WrappedKey wrappedKey =
+ WrappedKeyProto.WrappedKey.parseFrom(
+ tertiaries.get(applicationName).getWrappedKeyBytes());
+ unwrappedKeys.put(
+ applicationName, KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
+ }
+
+ return unwrappedKeys;
+ }
+
+ /**
+ * Adds all wrapped keys to the database.
+ *
+ * @throws IOException if an error occurred adding a wrapped key.
+ */
+ public void putAll(Map<String, WrappedKeyProto.WrappedKey> wrappedKeysByApplicationName)
+ throws IOException {
+ TertiaryKeysTable tertiaryKeysTable = mDatabase.getTertiaryKeysTable();
+ try {
+
+ for (String applicationName : wrappedKeysByApplicationName.keySet()) {
+ byte[] keyBytes = getEncodedKey(wrappedKeysByApplicationName.get(applicationName));
+ long primaryKey =
+ tertiaryKeysTable.addKey(
+ new TertiaryKey(
+ mSecondaryKey.getAlias(), applicationName, keyBytes));
+
+ if (primaryKey == -1) {
+ throw new IOException("Failed to commit to db");
+ }
+ }
+
+ } finally {
+ mDatabase.close();
+ }
+ }
+
+ private static void checkApplicationName(String applicationName) {
+ checkArgument(!applicationName.isEmpty(), "applicationName must not be empty string.");
+ checkArgument(!applicationName.contains("/"), "applicationName must not contain slash.");
+ }
+
+ private byte[] getEncodedKey(WrappedKeyProto.WrappedKey key) throws IOException {
+ byte[] buffer = new byte[key.getSerializedSize()];
+ CodedOutputByteBufferNano out = CodedOutputByteBufferNano.newInstance(buffer);
+ key.writeTo(out);
+ return buffer;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
new file mode 100644
index 000000000000..77cfded32173
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+/**
+ * Starts rotating to a new secondary key. Cannot complete until the screen is unlocked and the new
+ * key is synced.
+ */
+public class StartSecondaryKeyRotationTask {
+ private static final String TAG = "BE-StSecondaryKeyRotTsk";
+
+ private final CryptoSettings mCryptoSettings;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+
+ public StartSecondaryKeyRotationTask(
+ CryptoSettings cryptoSettings,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) {
+ mCryptoSettings = Preconditions.checkNotNull(cryptoSettings);
+ mSecondaryKeyManager = Preconditions.checkNotNull(secondaryKeyManager);
+ }
+
+ /** Begin the key rotation */
+ public void run() {
+ Slog.i(TAG, "Attempting to initiate a secondary key rotation.");
+
+ Optional<String> maybeCurrentAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
+ if (!maybeCurrentAlias.isPresent()) {
+ Slog.w(TAG, "No active current alias. Cannot trigger a secondary rotation.");
+ return;
+ }
+ String currentAlias = maybeCurrentAlias.get();
+
+ Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
+ if (maybeNextAlias.isPresent()) {
+ String nextAlias = maybeNextAlias.get();
+ if (nextAlias.equals(currentAlias)) {
+ // Shouldn't be possible, but guard against accidentally deleting the active key.
+ Slog.e(TAG, "Was already trying to rotate to what is already the active key.");
+ } else {
+ Slog.w(TAG, "Was already rotating to another key. Cancelling that.");
+ try {
+ mSecondaryKeyManager.remove(nextAlias);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Could not remove old key", e);
+ }
+ }
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+ }
+
+ RecoverableKeyStoreSecondaryKey newSecondaryKey;
+ try {
+ newSecondaryKey = mSecondaryKeyManager.generate();
+ } catch (LockScreenRequiredException e) {
+ Slog.e(TAG, "No lock screen is set - cannot generate a new key to rotate to.", e);
+ return;
+ } catch (InternalRecoveryServiceException e) {
+ Slog.e(TAG, "Internal error in Recovery Controller, failed to rotate key.", e);
+ return;
+ } catch (UnrecoverableKeyException e) {
+ Slog.e(TAG, "Failed to get key after generating, failed to rotate", e);
+ return;
+ }
+
+ String alias = newSecondaryKey.getAlias();
+ Slog.i(TAG, "Generated a new secondary key with alias '" + alias + "'.");
+ try {
+ mCryptoSettings.setNextSecondaryAlias(alias);
+ Slog.i(TAG, "Successfully set '" + alias + "' as next key to rotate to");
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unexpected error setting next alias", e);
+ try {
+ mSecondaryKeyManager.remove(alias);
+ } catch (Exception err) {
+ Slog.wtf(TAG, "Failed to remove generated key after encountering error", err);
+ }
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp
index 3376ec97e02f..4e42ce7366f0 100644
--- a/packages/BackupEncryption/test/robolectric/Android.bp
+++ b/packages/BackupEncryption/test/robolectric/Android.bp
@@ -23,6 +23,12 @@ android_robolectric_test {
"backup-encryption-protos",
"platform-test-annotations",
"testng",
+ "truth-prebuilt",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
],
instrumentation_for: "BackupEncryption",
}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java
new file mode 100644
index 000000000000..979b3d5dc13a
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/CryptoSettingsTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.Application;
+import android.security.keystore.recovery.RecoveryController;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Optional;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowRecoveryController.class)
+public class CryptoSettingsTest {
+
+ private static final String TEST_KEY_ALIAS =
+ "com.android.server.backup.encryption/keystore/08120c326b928ff34c73b9c58581da63";
+
+ private CryptoSettings mCryptoSettings;
+ private Application mApplication;
+
+ @Before
+ public void setUp() {
+ ShadowRecoveryController.reset();
+
+ mApplication = ApplicationProvider.getApplicationContext();
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
+ }
+
+ @Test
+ public void getActiveSecondaryAlias_isInitiallyAbsent() {
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent()).isFalse();
+ }
+
+ @Test
+ public void getActiveSecondaryAlias_returnsAliasIfKeyIsInRecoveryController() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.setActiveSecondaryKeyAlias(TEST_KEY_ALIAS);
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
+ }
+
+ @Test
+ public void getNextSecondaryAlias_isInitiallyAbsent() {
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
+ }
+
+ @Test
+ public void getNextSecondaryAlias_returnsAliasIfKeyIsInRecoveryController() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
+ }
+
+ @Test
+ public void isInitialized_isInitiallyFalse() {
+ assertThat(mCryptoSettings.getIsInitialized()).isFalse();
+ }
+
+ @Test
+ public void setActiveSecondaryAlias_throwsIfKeyIsNotInRecoveryController() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mCryptoSettings.setActiveSecondaryKeyAlias(TEST_KEY_ALIAS));
+ }
+
+ @Test
+ public void setNextSecondaryAlias_inRecoveryController_setsAlias() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+
+ mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
+ }
+
+ @Test
+ public void setNextSecondaryAlias_throwsIfKeyIsNotInRecoveryController() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS));
+ }
+
+ @Test
+ public void removeNextSecondaryAlias_removesIt() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.setNextSecondaryAlias(TEST_KEY_ALIAS);
+
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
+ }
+
+ @Test
+ public void initializeWithKeyAlias_setsAsInitialized() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
+ assertThat(mCryptoSettings.getIsInitialized()).isTrue();
+ }
+
+ @Test
+ public void initializeWithKeyAlias_setsActiveAlias() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(TEST_KEY_ALIAS);
+ }
+
+ @Test
+ public void initializeWithKeyAlias_throwsIfKeyIsNotInRecoveryController() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS));
+ }
+
+ @Test
+ public void initializeWithKeyAlias_throwsIfAlreadyInitialized() throws Exception {
+ setAliasIsInRecoveryController(TEST_KEY_ALIAS);
+ mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS);
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mCryptoSettings.initializeWithKeyAlias(TEST_KEY_ALIAS));
+ }
+
+ @Test
+ public void getSecondaryLastRotated_returnsEmptyInitially() {
+ assertThat(mCryptoSettings.getSecondaryLastRotated()).isEqualTo(Optional.empty());
+ }
+
+ @Test
+ public void getSecondaryLastRotated_returnsTimestampAfterItIsSet() {
+ long timestamp = 1000001;
+
+ mCryptoSettings.setSecondaryLastRotated(timestamp);
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(timestamp);
+ }
+
+ @Test
+ public void getAncestralSecondaryKeyVersion_notSet_returnsOptionalAbsent() {
+ assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().isPresent()).isFalse();
+ }
+
+ @Test
+ public void getAncestralSecondaryKeyVersion_isSet_returnsSetValue() {
+ String secondaryKeyVersion = "some_secondary_key";
+ mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion);
+
+ assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get())
+ .isEqualTo(secondaryKeyVersion);
+ }
+
+ @Test
+ public void getAncestralSecondaryKeyVersion_isSetMultipleTimes_returnsLastSetValue() {
+ String secondaryKeyVersion1 = "some_secondary_key";
+ String secondaryKeyVersion2 = "another_secondary_key";
+ mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion1);
+ mCryptoSettings.setAncestralSecondaryKeyVersion(secondaryKeyVersion2);
+
+ assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get())
+ .isEqualTo(secondaryKeyVersion2);
+ }
+
+ @Test
+ public void clearAllSettingsForBackup_clearsStateForBackup() throws Exception {
+ String key1 = "key1";
+ String key2 = "key2";
+ String ancestralKey = "ancestral_key";
+ setAliasIsInRecoveryController(key1);
+ setAliasIsInRecoveryController(key2);
+ mCryptoSettings.setActiveSecondaryKeyAlias(key1);
+ mCryptoSettings.setNextSecondaryAlias(key2);
+ mCryptoSettings.setSecondaryLastRotated(100001);
+ mCryptoSettings.setAncestralSecondaryKeyVersion(ancestralKey);
+
+ mCryptoSettings.clearAllSettingsForBackup();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent()).isFalse();
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
+ assertThat(mCryptoSettings.getSecondaryLastRotated().isPresent()).isFalse();
+ assertThat(mCryptoSettings.getAncestralSecondaryKeyVersion().get()).isEqualTo(ancestralKey);
+ }
+
+ private void setAliasIsInRecoveryController(String alias) throws Exception {
+ RecoveryController recoveryController = RecoveryController.getInstance(mApplication);
+ recoveryController.generateKey(alias);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java
new file mode 100644
index 000000000000..004f8097ce39
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RestoreKeyFetcherTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.InvalidKeyException;
+import java.security.KeyException;
+import java.security.SecureRandom;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+/** Test the restore key fetcher */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class RestoreKeyFetcherTest {
+
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+
+ private static final String TEST_SECONDARY_KEY_ALIAS = "test_2ndary_key";
+ private static final byte[] TEST_SECONDARY_KEY_BYTES = new byte[256 / Byte.SIZE];
+
+ @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+
+ /** Initialise the mocks **/
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /** Ensure the unwrap method works as expected */
+ @Test
+ public void unwrapTertiaryKey_returnsUnwrappedKey() throws Exception {
+ RecoverableKeyStoreSecondaryKey secondaryKey = createSecondaryKey();
+ SecretKey tertiaryKey = createTertiaryKey();
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey =
+ KeyWrapUtils.wrap(secondaryKey.getSecretKey(), tertiaryKey);
+ when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
+ .thenReturn(Optional.of(secondaryKey));
+
+ SecretKey actualTertiaryKey =
+ RestoreKeyFetcher.unwrapTertiaryKey(
+ () -> mSecondaryKeyManager,
+ TEST_SECONDARY_KEY_ALIAS,
+ wrappedTertiaryKey);
+
+ assertThat(actualTertiaryKey).isEqualTo(tertiaryKey);
+ }
+
+ /** Ensure that missing secondary keys are detected and an appropriate exception is thrown */
+ @Test
+ public void unwrapTertiaryKey_missingSecondaryKey_throwsSpecificException() throws Exception {
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey =
+ KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
+ when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS)).thenReturn(Optional.empty());
+
+ assertThrows(
+ KeyException.class,
+ () ->
+ RestoreKeyFetcher.unwrapTertiaryKey(
+ () -> mSecondaryKeyManager,
+ TEST_SECONDARY_KEY_ALIAS,
+ wrappedTertiaryKey));
+ }
+
+ /** Ensure that invalid secondary keys are detected and an appropriate exception is thrown */
+ @Test
+ public void unwrapTertiaryKey_badSecondaryKey_throws() throws Exception {
+ RecoverableKeyStoreSecondaryKey badSecondaryKey =
+ new RecoverableKeyStoreSecondaryKey(
+ TEST_SECONDARY_KEY_ALIAS,
+ new SecretKeySpec(new byte[] {0, 1}, KEY_GENERATOR_ALGORITHM));
+
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey =
+ KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
+ when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
+ .thenReturn(Optional.of(badSecondaryKey));
+
+ assertThrows(
+ InvalidKeyException.class,
+ () ->
+ RestoreKeyFetcher.unwrapTertiaryKey(
+ () -> mSecondaryKeyManager,
+ TEST_SECONDARY_KEY_ALIAS,
+ wrappedTertiaryKey));
+ }
+
+ private static RecoverableKeyStoreSecondaryKey createSecondaryKey() {
+ return new RecoverableKeyStoreSecondaryKey(
+ TEST_SECONDARY_KEY_ALIAS,
+ new SecretKeySpec(TEST_SECONDARY_KEY_BYTES, KEY_GENERATOR_ALGORITHM));
+ }
+
+ private static SecretKey createTertiaryKey() {
+ return new TertiaryKeyGenerator(new SecureRandom(new byte[] {0})).generate();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
new file mode 100644
index 000000000000..c31d19d8568c
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/SecondaryKeyRotationSchedulerTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.tasks.StartSecondaryKeyRotationTask;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.io.File;
+import java.time.Clock;
+
+@Config(shadows = SecondaryKeyRotationSchedulerTest.ShadowStartSecondaryKeyRotationTask.class)
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class SecondaryKeyRotationSchedulerTest {
+ private static final String SENTINEL_FILE_PATH = "force_secondary_key_rotation";
+
+ @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ @Mock private Clock mClock;
+
+ private CryptoSettings mCryptoSettings;
+ private SecondaryKeyRotationScheduler mScheduler;
+ private long mRotationIntervalMillis;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context application = ApplicationProvider.getApplicationContext();
+
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(application);
+ mRotationIntervalMillis = mCryptoSettings.backupSecondaryKeyRotationIntervalMs();
+
+ mScheduler =
+ new SecondaryKeyRotationScheduler(
+ application, mSecondaryKeyManager, mCryptoSettings, mClock);
+ ShadowStartSecondaryKeyRotationTask.reset();
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfRotationWasFarEnoughInThePast() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsNewRotationTimeIfRotationWasFarEnoughInThePast() {
+ long lastRotated = 100009;
+ long now = lastRotated + mRotationIntervalMillis;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfClockHasChanged() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_rotatesIfSentinelFileIsPresent() throws Exception {
+ File file = new File(RuntimeEnvironment.application.getFilesDir(), SENTINEL_FILE_PATH);
+ file.createNewFile();
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isTrue();
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsNextRotationIfClockHasChanged() {
+ long lastRotated = 100009;
+ long now = lastRotated - 1;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ @Test
+ public void startRotationIfScheduled_doesNothingIfRotationWasRecentEnough() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(ShadowStartSecondaryKeyRotationTask.sRan).isFalse();
+ }
+
+ @Test
+ public void startRotationIfScheduled_doesNotSetRotationTimeIfRotationWasRecentEnough() {
+ long lastRotated = 100009;
+ mCryptoSettings.setSecondaryLastRotated(lastRotated);
+ setNow(lastRotated + mRotationIntervalMillis - 1);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(lastRotated);
+ }
+
+ @Test
+ public void startRotationIfScheduled_setsLastRotatedToNowIfNeverRotated() {
+ long now = 13295436;
+ setNow(now);
+
+ mScheduler.startRotationIfScheduled();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().get()).isEqualTo(now);
+ }
+
+ private void setNow(long timestamp) {
+ when(mClock.millis()).thenReturn(timestamp);
+ }
+
+ @Implements(StartSecondaryKeyRotationTask.class)
+ public static class ShadowStartSecondaryKeyRotationTask {
+ private static boolean sRan = false;
+
+ @Implementation
+ public void run() {
+ sRan = true;
+ }
+
+ @Resetter
+ public static void reset() {
+ sRan = false;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java
new file mode 100644
index 000000000000..1ed8309c3a48
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyManagerTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+
+import javax.crypto.SecretKey;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowRecoveryController.class)
+public class TertiaryKeyManagerTest {
+
+ private static final String TEST_PACKAGE_1 = "com.example.app1";
+ private static final String TEST_PACKAGE_2 = "com.example.app2";
+
+ private SecureRandom mSecureRandom;
+ private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+
+ @Mock private TertiaryKeyRotationScheduler mTertiaryKeyRotationScheduler;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mSecureRandom = new SecureRandom();
+ mSecondaryKey =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(application), mSecureRandom)
+ .generate();
+ ShadowRecoveryController.reset();
+ }
+
+ private TertiaryKeyManager createNewManager(String packageName) {
+ return new TertiaryKeyManager(
+ application,
+ mSecureRandom,
+ mTertiaryKeyRotationScheduler,
+ mSecondaryKey,
+ packageName);
+ }
+
+ @Test
+ public void getKey_noExistingKey_returnsNewKey() throws Exception {
+ assertThat(createNewManager(TEST_PACKAGE_1).getKey()).isNotNull();
+ }
+
+ @Test
+ public void getKey_noExistingKey_recordsIncrementalBackup() throws Exception {
+ createNewManager(TEST_PACKAGE_1).getKey();
+ verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
+ }
+
+ @Test
+ public void getKey_existingKey_returnsExistingKey() throws Exception {
+ TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
+ SecretKey existingKey = manager.getKey();
+
+ assertThat(manager.getKey()).isEqualTo(existingKey);
+ }
+
+ @Test
+ public void getKey_existingKey_recordsBackupButNotRotation() throws Exception {
+ createNewManager(TEST_PACKAGE_1).getKey();
+ reset(mTertiaryKeyRotationScheduler);
+
+ createNewManager(TEST_PACKAGE_1).getKey();
+
+ verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
+ verify(mTertiaryKeyRotationScheduler, never()).recordKeyRotation(any());
+ }
+
+ @Test
+ public void getKey_existingKeyButRotationRequired_returnsNewKey() throws Exception {
+ SecretKey firstKey = createNewManager(TEST_PACKAGE_1).getKey();
+ when(mTertiaryKeyRotationScheduler.isKeyRotationDue(TEST_PACKAGE_1)).thenReturn(true);
+
+ SecretKey secondKey = createNewManager(TEST_PACKAGE_1).getKey();
+
+ assertThat(secondKey).isNotEqualTo(firstKey);
+ }
+
+ @Test
+ public void getKey_existingKeyButRotationRequired_recordsKeyRotationAndBackup()
+ throws Exception {
+ when(mTertiaryKeyRotationScheduler.isKeyRotationDue(TEST_PACKAGE_1)).thenReturn(true);
+ createNewManager(TEST_PACKAGE_1).getKey();
+
+ InOrder inOrder = inOrder(mTertiaryKeyRotationScheduler);
+ inOrder.verify(mTertiaryKeyRotationScheduler).recordKeyRotation(TEST_PACKAGE_1);
+ inOrder.verify(mTertiaryKeyRotationScheduler).recordBackup(TEST_PACKAGE_1);
+ }
+
+ @Test
+ public void getKey_twoApps_returnsDifferentKeys() throws Exception {
+ TertiaryKeyManager firstManager = createNewManager(TEST_PACKAGE_1);
+ TertiaryKeyManager secondManager = createNewManager(TEST_PACKAGE_2);
+ SecretKey firstKey = firstManager.getKey();
+
+ assertThat(secondManager.getKey()).isNotEqualTo(firstKey);
+ }
+
+ @Test
+ public void getWrappedKey_noExistingKey_returnsWrappedNewKey() throws Exception {
+ TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
+ SecretKey unwrappedKey = manager.getKey();
+ WrappedKeyProto.WrappedKey wrappedKey = manager.getWrappedKey();
+
+ SecretKey expectedUnwrappedKey =
+ KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey);
+ assertThat(unwrappedKey).isEqualTo(expectedUnwrappedKey);
+ }
+
+ @Test
+ public void getWrappedKey_existingKey_returnsWrappedExistingKey() throws Exception {
+ TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
+ WrappedKeyProto.WrappedKey wrappedKey = manager.getWrappedKey();
+ SecretKey unwrappedKey = manager.getKey();
+
+ SecretKey expectedUnwrappedKey =
+ KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey);
+ assertThat(unwrappedKey).isEqualTo(expectedUnwrappedKey);
+ }
+
+ @Test
+ public void wasKeyRotated_noExistingKey_returnsTrue() throws Exception {
+ TertiaryKeyManager manager = createNewManager(TEST_PACKAGE_1);
+ assertThat(manager.wasKeyRotated()).isTrue();
+ }
+
+ @Test
+ public void wasKeyRotated_existingKey_returnsFalse() throws Exception {
+ createNewManager(TEST_PACKAGE_1).getKey();
+ assertThat(createNewManager(TEST_PACKAGE_1).wasKeyRotated()).isFalse();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java
new file mode 100644
index 000000000000..dfc7e2bfd4f7
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationSchedulerTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.File;
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/** Tests for the tertiary key rotation scheduler */
+@RunWith(RobolectricTestRunner.class)
+public final class TertiaryKeyRotationSchedulerTest {
+
+ private static final int MAXIMUM_ROTATIONS_PER_WINDOW = 2;
+ private static final int MAX_BACKUPS_TILL_ROTATION = 31;
+ private static final String SHARED_PREFS_NAME = "tertiary_key_rotation_tracker";
+ private static final String PACKAGE_1 = "com.android.example1";
+ private static final String PACKAGE_2 = "com.android.example2";
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Mock private Clock mClock;
+
+ private File mFile;
+ private TertiaryKeyRotationScheduler mScheduler;
+
+ /** Setup the scheduler for test */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mFile = temporaryFolder.newFile();
+ mScheduler =
+ new TertiaryKeyRotationScheduler(
+ new TertiaryKeyRotationTracker(
+ application.getSharedPreferences(
+ SHARED_PREFS_NAME, Context.MODE_PRIVATE),
+ MAX_BACKUPS_TILL_ROTATION),
+ new TertiaryKeyRotationWindowedCount(mFile, mClock),
+ MAXIMUM_ROTATIONS_PER_WINDOW);
+ }
+
+ /** Test we don't trigger a rotation straight off */
+ @Test
+ public void isKeyRotationDue_isFalseInitially() {
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /** Test we don't prematurely trigger a rotation */
+ @Test
+ public void isKeyRotationDue_isFalseAfterInsufficientBackups() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION - 1);
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /** Test we do trigger a backup */
+ @Test
+ public void isKeyRotationDue_isTrueAfterEnoughBackups() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION);
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
+ }
+
+ /** Test rotation will occur if the quota allows */
+ @Test
+ public void isKeyRotationDue_isTrueIfRotationQuotaRemainsInWindow() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION);
+ mScheduler.recordKeyRotation(PACKAGE_2);
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
+ }
+
+ /** Test rotation is blocked if the quota has been exhausted */
+ @Test
+ public void isKeyRotationDue_isFalseIfEnoughRotationsHaveHappenedInWindow() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION);
+ mScheduler.recordKeyRotation(PACKAGE_2);
+ mScheduler.recordKeyRotation(PACKAGE_2);
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /** Test rotation is due after one window has passed */
+ @Test
+ public void isKeyRotationDue_isTrueAfterAWholeWindowHasPassed() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION);
+ mScheduler.recordKeyRotation(PACKAGE_2);
+ mScheduler.recordKeyRotation(PACKAGE_2);
+ setTimeMillis(TimeUnit.HOURS.toMillis(24));
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isTrue();
+ }
+
+ /** Test the rotation state changes after a rotation */
+ @Test
+ public void isKeyRotationDue_isFalseAfterRotation() {
+ simulateBackups(MAX_BACKUPS_TILL_ROTATION);
+ mScheduler.recordKeyRotation(PACKAGE_1);
+ assertThat(mScheduler.isKeyRotationDue(PACKAGE_1)).isFalse();
+ }
+
+ /** Test the rate limiting for a given window */
+ @Test
+ public void isKeyRotationDue_neverAllowsMoreThanInWindow() {
+ List<String> apps = makeTestApps(MAXIMUM_ROTATIONS_PER_WINDOW * MAX_BACKUPS_TILL_ROTATION);
+
+ // simulate backups of all apps each night
+ for (int i = 0; i < 300; i++) {
+ setTimeMillis(i * TimeUnit.HOURS.toMillis(24));
+ int rotationsThisNight = 0;
+ for (String app : apps) {
+ if (mScheduler.isKeyRotationDue(app)) {
+ rotationsThisNight++;
+ mScheduler.recordKeyRotation(app);
+ } else {
+ mScheduler.recordBackup(app);
+ }
+ }
+ assertThat(rotationsThisNight).isAtMost(MAXIMUM_ROTATIONS_PER_WINDOW);
+ }
+ }
+
+ /** Test that backups are staggered over the window */
+ @Test
+ public void isKeyRotationDue_naturallyStaggersBackupsOverTime() {
+ List<String> apps = makeTestApps(MAXIMUM_ROTATIONS_PER_WINDOW * MAX_BACKUPS_TILL_ROTATION);
+
+ HashMap<String, ArrayList<Integer>> rotationDays = new HashMap<>();
+ for (String app : apps) {
+ rotationDays.put(app, new ArrayList<>());
+ }
+
+ // simulate backups of all apps each night
+ for (int i = 0; i < 300; i++) {
+ setTimeMillis(i * TimeUnit.HOURS.toMillis(24));
+ for (String app : apps) {
+ if (mScheduler.isKeyRotationDue(app)) {
+ rotationDays.get(app).add(i);
+ mScheduler.recordKeyRotation(app);
+ } else {
+ mScheduler.recordBackup(app);
+ }
+ }
+ }
+
+ for (String app : apps) {
+ List<Integer> days = rotationDays.get(app);
+ for (int i = 1; i < days.size(); i++) {
+ assertThat(days.get(i) - days.get(i - 1)).isEqualTo(MAX_BACKUPS_TILL_ROTATION + 1);
+ }
+ }
+ }
+
+ private ArrayList<String> makeTestApps(int n) {
+ ArrayList<String> apps = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ apps.add(String.format(Locale.US, "com.android.app%d", i));
+ }
+ return apps;
+ }
+
+ private void simulateBackups(int numberOfBackups) {
+ while (numberOfBackups > 0) {
+ mScheduler.recordBackup(PACKAGE_1);
+ numberOfBackups--;
+ }
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ when(mClock.millis()).thenReturn(timeMillis);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java
new file mode 100644
index 000000000000..bd309779f303
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationWindowedCountTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Clock;
+import java.util.concurrent.TimeUnit;
+
+/** Tests for {@link TertiaryKeyRotationWindowedCount}. */
+@RunWith(RobolectricTestRunner.class)
+public class TertiaryKeyRotationWindowedCountTest {
+ private static final int TIMESTAMP_SIZE_IN_BYTES = 8;
+
+ @Rule public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Mock private Clock mClock;
+
+ private File mFile;
+ private TertiaryKeyRotationWindowedCount mWindowedcount;
+
+ /** Setup the windowed counter for testing */
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+ mFile = mTemporaryFolder.newFile();
+ mWindowedcount = new TertiaryKeyRotationWindowedCount(mFile, mClock);
+ }
+
+ /** Test handling bad files */
+ @Test
+ public void constructor_doesNotFailForBadFile() throws IOException {
+ new TertiaryKeyRotationWindowedCount(mTemporaryFolder.newFolder(), mClock);
+ }
+
+ /** Test the count is 0 to start */
+ @Test
+ public void getCount_isZeroInitially() {
+ assertThat(mWindowedcount.getCount()).isEqualTo(0);
+ }
+
+ /** Test the count is correct for a time window */
+ @Test
+ public void getCount_includesResultsInLastTwentyFourHours() {
+ setTimeMillis(0);
+ mWindowedcount.record();
+ setTimeMillis(TimeUnit.HOURS.toMillis(4));
+ mWindowedcount.record();
+ setTimeMillis(TimeUnit.HOURS.toMillis(23));
+ mWindowedcount.record();
+ mWindowedcount.record();
+ assertThat(mWindowedcount.getCount()).isEqualTo(4);
+ }
+
+ /** Test old results are ignored */
+ @Test
+ public void getCount_ignoresResultsOlderThanTwentyFourHours() {
+ setTimeMillis(0);
+ mWindowedcount.record();
+ setTimeMillis(TimeUnit.HOURS.toMillis(24));
+ assertThat(mWindowedcount.getCount()).isEqualTo(0);
+ }
+
+ /** Test future events are removed if the clock moves backways (e.g. DST, TZ change) */
+ @Test
+ public void getCount_removesFutureEventsIfClockHasChanged() {
+ setTimeMillis(1000);
+ mWindowedcount.record();
+ setTimeMillis(0);
+ assertThat(mWindowedcount.getCount()).isEqualTo(0);
+ }
+
+ /** Check recording doesn't fail for a bad file */
+ @Test
+ public void record_doesNotFailForBadFile() throws Exception {
+ new TertiaryKeyRotationWindowedCount(mTemporaryFolder.newFolder(), mClock).record();
+ }
+
+ /** Checks the state is persisted */
+ @Test
+ public void record_persistsStateToDisk() {
+ setTimeMillis(0);
+ mWindowedcount.record();
+ assertThat(new TertiaryKeyRotationWindowedCount(mFile, mClock).getCount()).isEqualTo(1);
+ }
+
+ /** Test the file doesn't contain unnecessary data */
+ @Test
+ public void record_compactsFileToLast24Hours() {
+ setTimeMillis(0);
+ mWindowedcount.record();
+ assertThat(mFile.length()).isEqualTo(TIMESTAMP_SIZE_IN_BYTES);
+ setTimeMillis(1);
+ mWindowedcount.record();
+ assertThat(mFile.length()).isEqualTo(2 * TIMESTAMP_SIZE_IN_BYTES);
+ setTimeMillis(TimeUnit.HOURS.toMillis(24));
+ mWindowedcount.record();
+ assertThat(mFile.length()).isEqualTo(2 * TIMESTAMP_SIZE_IN_BYTES);
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ when(mClock.millis()).thenReturn(timeMillis);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java
new file mode 100644
index 000000000000..ccc5f32dad36
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyStoreTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
+
+import android.content.Context;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.security.InvalidKeyException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+/** Tests for the tertiary key store */
+@RunWith(RobolectricTestRunner.class)
+public class TertiaryKeyStoreTest {
+
+ private static final String SECONDARY_KEY_ALIAS = "Robbo/Ranx";
+
+ private Context mApplication;
+ private TertiaryKeyStore mTertiaryKeyStore;
+ private SecretKey mSecretKey;
+
+ /** Initialise the keystore for testing */
+ @Before
+ public void setUp() throws Exception {
+ mApplication = RuntimeEnvironment.application;
+ mSecretKey = generateAesKey();
+ mTertiaryKeyStore =
+ TertiaryKeyStore.newInstance(
+ mApplication,
+ new RecoverableKeyStoreSecondaryKey(SECONDARY_KEY_ALIAS, mSecretKey));
+ }
+
+ /** Test a reound trip for a key */
+ @Test
+ public void load_loadsAKeyThatWasSaved() throws Exception {
+ String packageName = "com.android.example";
+ SecretKey packageKey = generateAesKey();
+ mTertiaryKeyStore.save(packageName, packageKey);
+
+ Optional<SecretKey> maybeLoadedKey = mTertiaryKeyStore.load(packageName);
+
+ assertTrue(maybeLoadedKey.isPresent());
+ assertEquals(packageKey, maybeLoadedKey.get());
+ }
+
+ /** Test isolation between packages */
+ @Test
+ public void load_doesNotLoadAKeyForAnotherSecondary() throws Exception {
+ String packageName = "com.android.example";
+ SecretKey packageKey = generateAesKey();
+ mTertiaryKeyStore.save(packageName, packageKey);
+ TertiaryKeyStore managerWithOtherSecondaryKey =
+ TertiaryKeyStore.newInstance(
+ mApplication,
+ new RecoverableKeyStoreSecondaryKey(
+ "myNewSecondaryKeyAlias", generateAesKey()));
+
+ assertFalse(managerWithOtherSecondaryKey.load(packageName).isPresent());
+ }
+
+ /** Test non-existent key handling */
+ @Test
+ public void load_returnsAbsentForANonExistentKey() throws Exception {
+ assertFalse(mTertiaryKeyStore.load("mystery.package").isPresent());
+ }
+
+ /** Test handling incorrect keys */
+ @Test
+ public void load_throwsIfHasWrongBackupKey() throws Exception {
+ String packageName = "com.android.example";
+ SecretKey packageKey = generateAesKey();
+ mTertiaryKeyStore.save(packageName, packageKey);
+ TertiaryKeyStore managerWithBadKey =
+ TertiaryKeyStore.newInstance(
+ mApplication,
+ new RecoverableKeyStoreSecondaryKey(SECONDARY_KEY_ALIAS, generateAesKey()));
+
+ assertThrows(InvalidKeyException.class, () -> managerWithBadKey.load(packageName));
+ }
+
+ /** Test handling of empty app name */
+ @Test
+ public void load_throwsForEmptyApplicationName() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> mTertiaryKeyStore.load(""));
+ }
+
+ /** Test handling of an invalid app name */
+ @Test
+ public void load_throwsForBadApplicationName() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mTertiaryKeyStore.load("com/android/example"));
+ }
+
+ /** Test key replacement */
+ @Test
+ public void save_overwritesPreviousKey() throws Exception {
+ String packageName = "com.android.example";
+ SecretKey oldKey = generateAesKey();
+ mTertiaryKeyStore.save(packageName, oldKey);
+ SecretKey newKey = generateAesKey();
+
+ mTertiaryKeyStore.save(packageName, newKey);
+
+ Optional<SecretKey> maybeLoadedKey = mTertiaryKeyStore.load(packageName);
+ assertTrue(maybeLoadedKey.isPresent());
+ SecretKey loadedKey = maybeLoadedKey.get();
+ assertThat(loadedKey).isNotEqualTo(oldKey);
+ assertThat(loadedKey).isEqualTo(newKey);
+ }
+
+ /** Test saving with an empty application name fails */
+ @Test
+ public void save_throwsForEmptyApplicationName() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class, () -> mTertiaryKeyStore.save("", generateAesKey()));
+ }
+
+ /** Test saving an invalid application name fails */
+ @Test
+ public void save_throwsForBadApplicationName() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mTertiaryKeyStore.save("com/android/example", generateAesKey()));
+ }
+
+ /** Test handling an empty database */
+ @Test
+ public void getAll_returnsEmptyMapForEmptyDb() throws Exception {
+ assertThat(mTertiaryKeyStore.getAll()).isEmpty();
+ }
+
+ /** Test loading all available keys works as expected */
+ @Test
+ public void getAll_returnsAllKeysSaved() throws Exception {
+ String package1 = "com.android.example";
+ SecretKey key1 = generateAesKey();
+ String package2 = "com.anndroid.example1";
+ SecretKey key2 = generateAesKey();
+ String package3 = "com.android.example2";
+ SecretKey key3 = generateAesKey();
+ mTertiaryKeyStore.save(package1, key1);
+ mTertiaryKeyStore.save(package2, key2);
+ mTertiaryKeyStore.save(package3, key3);
+
+ Map<String, SecretKey> keys = mTertiaryKeyStore.getAll();
+
+ assertThat(keys).containsExactly(package1, key1, package2, key2, package3, key3);
+ }
+
+ /** Test cross-secondary isolation */
+ @Test
+ public void getAll_doesNotReturnKeysForOtherSecondary() throws Exception {
+ String packageName = "com.android.example";
+ TertiaryKeyStore managerWithOtherSecondaryKey =
+ TertiaryKeyStore.newInstance(
+ mApplication,
+ new RecoverableKeyStoreSecondaryKey(
+ "myNewSecondaryKeyAlias", generateAesKey()));
+ managerWithOtherSecondaryKey.save(packageName, generateAesKey());
+
+ assertThat(mTertiaryKeyStore.getAll()).isEmpty();
+ }
+
+ /** Test mass put into the keystore */
+ @Test
+ public void putAll_putsAllWrappedKeysInTheStore() throws Exception {
+ String packageName = "com.android.example";
+ SecretKey key = generateAesKey();
+ WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(mSecretKey, key);
+
+ Map<String, WrappedKeyProto.WrappedKey> testElements = new HashMap<>();
+ testElements.put(packageName, wrappedKey);
+ mTertiaryKeyStore.putAll(testElements);
+
+ assertThat(mTertiaryKeyStore.getAll()).containsKey(packageName);
+ assertThat(mTertiaryKeyStore.getAll().get(packageName).getEncoded())
+ .isEqualTo(key.getEncoded());
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
new file mode 100644
index 000000000000..4ac4fa8d06c9
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTaskTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRecoveryController.class})
+@Presubmit
+public class StartSecondaryKeyRotationTaskTest {
+
+ private CryptoSettings mCryptoSettings;
+ private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private StartSecondaryKeyRotationTask mStartSecondaryKeyRotationTask;
+
+ @Before
+ public void setUp() throws Exception {
+ mSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(RuntimeEnvironment.application),
+ new SecureRandom());
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(RuntimeEnvironment.application);
+ mStartSecondaryKeyRotationTask =
+ new StartSecondaryKeyRotationTask(mCryptoSettings, mSecondaryKeyManager);
+
+ ShadowRecoveryController.reset();
+ }
+
+ @Test
+ public void run_doesNothingIfNoActiveSecondaryExists() {
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isFalse();
+ }
+
+ @Test
+ public void run_doesNotRemoveExistingNextSecondaryKeyIfItIsAlreadyActive() throws Exception {
+ generateAnActiveKey();
+ String activeAlias = mCryptoSettings.getActiveSecondaryKeyAlias().get();
+ mCryptoSettings.setNextSecondaryAlias(activeAlias);
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mSecondaryKeyManager.get(activeAlias).isPresent()).isTrue();
+ }
+
+ @Test
+ public void run_doesRemoveExistingNextSecondaryKeyIfItIsNotYetActive() throws Exception {
+ generateAnActiveKey();
+ RecoverableKeyStoreSecondaryKey nextKey = mSecondaryKeyManager.generate();
+ String nextAlias = nextKey.getAlias();
+ mCryptoSettings.setNextSecondaryAlias(nextAlias);
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isFalse();
+ }
+
+ @Test
+ public void run_generatesANewNextSecondaryKey() throws Exception {
+ generateAnActiveKey();
+
+ mStartSecondaryKeyRotationTask.run();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().isPresent()).isTrue();
+ }
+
+ @Test
+ public void run_generatesANewKeyThatExistsInKeyStore() throws Exception {
+ generateAnActiveKey();
+
+ mStartSecondaryKeyRotationTask.run();
+
+ String nextAlias = mCryptoSettings.getNextSecondaryKeyAlias().get();
+ assertThat(mSecondaryKeyManager.get(nextAlias).isPresent()).isTrue();
+ }
+
+ private void generateAnActiveKey() throws Exception {
+ RecoverableKeyStoreSecondaryKey secondaryKey = mSecondaryKeyManager.generate();
+ mCryptoSettings.setActiveSecondaryKeyAlias(secondaryKey.getAlias());
+ }
+}
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index bbae9ff51615..672879ae6e9d 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -13,18 +13,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-android_app {
- name: "CarSystemUI",
-
- overrides: [
- "SystemUI",
- ],
+android_library {
+ name: "CarSystemUI-core",
srcs: [
"src/**/*.java",
"src/**/I*.aidl",
],
+ resource_dirs: [
+ "res-keyguard",
+ "res",
+ ],
+
static_libs: [
"SystemUI-core",
"CarNotificationLib",
@@ -58,6 +59,28 @@ android_app {
manifest: "AndroidManifest.xml",
+ plugins: ["dagger2-compiler-2.19"],
+
+}
+
+android_app {
+ name: "CarSystemUI",
+
+ static_libs: [
+ "CarSystemUI-core",
+ ],
+
+ libs: [
+ "telephony-common",
+ "android.car",
+ ],
+
+ resource_dirs: [],
+
+ overrides: [
+ "SystemUI",
+ ],
+
platform_apis: true,
product_specific: true,
certificate: "platform",
@@ -68,12 +91,6 @@ android_app {
"proguard.flags",
],
},
- resource_dirs: [
- "res-keyguard",
- "res",
- ],
-
-
dxflags: ["--multi-dex"],
aaptflags: [
@@ -81,6 +98,8 @@ android_app {
"com.android.keyguard",
],
+ kotlincflags: ["-Xjvm-default=enable"],
+
plugins: ["dagger2-compiler-2.19"],
required: ["privapp_whitelist_com.android.systemui"],
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index e0ae45662390..5fcf38fce3cc 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -32,7 +32,7 @@
<color name="system_bar_background_opaque">#ff172026</color>
<color name="status_bar_background_color">#33000000</color>
- <drawable name="system_bar_background">@android:color/transparent</drawable>
+ <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
<!-- The background color of the notification shade -->
<color name="notification_shade_background_color">#D6000000</color>
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index b0e2700a1f30..1c0e409dfc35 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -62,7 +62,7 @@
android:exported="false" />
<activity android:name=".DeleteStagedFileOnResult"
- android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 55ff591e3a1f..a2fa46130076 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -135,6 +135,7 @@ public class PackageInstallerActivity extends AlertActivity {
mEnableOk = true;
mOk.setEnabled(true);
+ mOk.setFilterTouchesWhenObscured(true);
}
/**
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 7846be161c0f..363d88544a03 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1604061903696928905">"Definições de pesquisa"</string>
+ <string name="search_menu" msgid="1604061903696928905">"Pesquisar definições"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 13890e029274..f1fc9f9e3d4e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -452,7 +452,7 @@
<string name="cancel" msgid="6859253417269739139">"বাতিল"</string>
<string name="okay" msgid="1997666393121016642">"ঠিক আছে"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"চালু করুন"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবেন না\' মোড চালু করুন"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"কখনও নয়"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"শুধুমাত্র অগ্রাধিকার"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index beb1ac546949..7874aeb02996 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -141,7 +141,7 @@
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string>
<string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string>
- <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión USB"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión a red por USB"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
<string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión Bluetooth"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir conexión"</string>
@@ -286,7 +286,7 @@
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"Esperar que se conecte el depurador para iniciar la aplicación"</string>
<string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
- <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Representación acelerada mediante hardware"</string>
+ <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Procesamiento acelerado mediante hardware"</string>
<string name="media_category" msgid="4388305075496848353">"Multimedia"</string>
<string name="debug_monitoring_category" msgid="7640508148375798343">"Supervisión"</string>
<string name="strict_mode" msgid="1938795874357830695">"Modo estricto"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 5ad9b0191c77..3d9a78e99204 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -76,7 +76,7 @@
<item msgid="3422726142222090896">"avrcp16"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="7065842274271279580">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="7065842274271279580">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="7539690996561263909">"SBC"</item>
<item msgid="686685526567131661">"AAC"</item>
<item msgid="5254942598247222737">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
@@ -86,7 +86,7 @@
<item msgid="3304843301758635896">"वैकल्पिक कोडेक अक्षम करें"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="5062108632402595000">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="5062108632402595000">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="6898329690939802290">"SBC"</item>
<item msgid="6839647709301342559">"AAC"</item>
<item msgid="7848030269621918608">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
@@ -96,38 +96,38 @@
<item msgid="741805482892725657">"वैकल्पिक कोडेक अक्षम करें"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item msgid="3093023430402746802">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="3093023430402746802">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="8895532488906185219">"44.1 kHz"</item>
<item msgid="2909915718994807056">"48.0 kHz"</item>
<item msgid="3347287377354164611">"88.2 kHz"</item>
<item msgid="1234212100239985373">"96.0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="3214516120190965356">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="3214516120190965356">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="4482862757811638365">"44.1 kHz"</item>
<item msgid="354495328188724404">"48.0 kHz"</item>
<item msgid="7329816882213695083">"88.2 kHz"</item>
<item msgid="6967397666254430476">"96.0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2684127272582591429">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="2684127272582591429">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="5618929009984956469">"16 बिट/नमूना"</item>
<item msgid="3412640499234627248">"24 बिट/नमूना"</item>
<item msgid="121583001492929387">"32 बिट/नमूना"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="1081159789834584363">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="1081159789834584363">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="4726688794884191540">"16 बिट/नमूना"</item>
<item msgid="305344756485516870">"24 बिट/नमूना"</item>
<item msgid="244568657919675099">"32 बिट/नमूना"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item msgid="5226878858503393706">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="5226878858503393706">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="4106832974775067314">"मोनो"</item>
<item msgid="5571632958424639155">"स्टीरियो"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="4118561796005528173">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="4118561796005528173">"सिस्टम चुनाव का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="8900559293912978337">"मोनो"</item>
<item msgid="8883739882299884241">"स्टीरियो"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 5d512a84ef99..3a20d04d7ab2 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -46,7 +46,7 @@
<string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट कनेक्शन नहीं है"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करना ज़रूरी है"</string>
- <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
+ <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ऐक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
<string name="connected_via_carrier" msgid="7583780074526041912">"%1$s के ज़रिए कनेक्ट"</string>
<string name="available_via_carrier" msgid="1469036129740799053">"%1$s के ज़रिए उपलब्ध"</string>
<string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोला जा रहा है"</string>
@@ -68,7 +68,7 @@
<string name="bluetooth_pairing" msgid="1426882272690346242">"युग्‍मित कर रहा है…"</string>
<string name="bluetooth_connected_no_headset" msgid="616068069034994802">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"जुड़ गया (मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का एक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का ऐक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"जुड़ गया (फ़ोन या मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"जुड़ गया, बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
@@ -88,7 +88,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"संपर्क साझाकरण के लिए उपयोग करें"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इंटरनेट कनेक्शन साझाकरण"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"लेख संदेश"</string>
- <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम एक्सेस"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडियो"</string>
<string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"सुनने में मदद करने वाले डिवाइस"</string>
@@ -200,7 +200,7 @@
<string name="development_settings_not_available" msgid="4308569041701535607">"यह उपयोगकर्ता, डेवलपर के लिए सेटिंग और टूल का इस्तेमाल नहीं कर सकता"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"VPN सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"टेदरिंग सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string>
- <string name="apn_settings_not_available" msgid="7873729032165324000">"एक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string>
+ <string name="apn_settings_not_available" msgid="7873729032165324000">"ऐक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string>
<string name="enable_adb" msgid="7982306934419797485">"USB डीबग करना"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"डीबग मोड जब USB कनेक्‍ट किया गया हो"</string>
<string name="clear_adb_keys" msgid="4038889221503122743">"USB डीबग करने की मंज़ूरी रद्द करें"</string>
@@ -361,7 +361,7 @@
<string name="runningservices_settings_summary" msgid="854608995821032748">"इस समय चल रही सेवाओं को देखें और नियंत्रित करें"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"वेबव्यू लागू करें"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"वेबव्यू सेट करें"</string>
- <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"यह चयन अब मान्य नहीं है. पुनः प्रयास करें."</string>
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"यह चुनाव अब मान्य नहीं है. दोबारा कोशिश करें."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फ़ाइल आधारित सुरक्षित करने के तरीके में बदलें"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करें..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फ़ाइल पहले से एन्क्रिप्ट की हुई है"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"बंद किया गया"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"अनुमति है"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति नहीं है"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का एक्सेस"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का ऐक्सेस"</string>
<string name="home" msgid="3256884684164448244">"सेटिंग का होम पेज"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 5cffafed6689..7368f1d105f2 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -43,7 +43,7 @@
<item msgid="8937994881315223448">"Միացված է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին"</item>
<item msgid="1330262655415760617">"Անջատված"</item>
<item msgid="7698638434317271902">"Անջատվում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
- <item msgid="197508606402264311">"Անջատած է"</item>
+ <item msgid="197508606402264311">"Անջատված է"</item>
<item msgid="8578370891960825148">"Անհաջող"</item>
<item msgid="5660739516542454527">"Արգելափակված"</item>
<item msgid="1805837518286731242">"Վատ ցանցից ժամանակավոր խուսափում"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index bf587405c668..a74b4ae3ae9e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -263,7 +263,7 @@
<string name="debug_view_attributes" msgid="6485448367803310384">"Միացնել ցուցադրման հատկանիշների ստուգումը"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Միշտ ակտիվացրած պահել բջջային տվյալները, նույնիսկ Wi‑Fi-ը միացրած ժամանակ (ցանցերի միջև արագ փոխարկման համար):"</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Օգտագործել սարքակազմի արագացման միացումը, եթե հասանելի է"</string>
- <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB-ի վրիպազերծումը:"</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB վրիպազերծումը:"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"Փակե՞լ USB-ի վրիպազերծման մուտքը` անջատելով այն բոլոր համակարգիչներից, որտեղ նախկինում թույլատրել էիք:"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Ընդունե՞լ ծրագրավորման կարգավորումներ:"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index e5d29bff8b65..50543d8bfbee 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -385,10 +385,10 @@
<string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"残り時間: 約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(使用状況に基づく)(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<!-- no translation found for power_remaining_duration_only_short (9183070574408359726) -->
<skip />
- <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"電池切れの推定時刻: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時刻: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時刻: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時刻: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
<string name="power_suggestion_extend_battery" msgid="4401408879069551485">"<xliff:g id="TIME">%1$s</xliff:g>まで電池消費量を抑える"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fd45cb0a1f3c..f1934c37369e 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -248,7 +248,7 @@
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
<string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ब्याट्रीको खपत कम गरी नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
- <string name="wifi_metered_label" msgid="4514924227256839725">"मिटर गरिएको जडान भनी चिन्ह लगाइएको"</string>
+ <string name="wifi_metered_label" msgid="4514924227256839725">"सशुल्क वाइफाइ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"मिटर नगरिएको"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लगर बफर आकारहरू"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"लग बफर प्रति लगर आकार चयन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index cf442b73e721..0073003bdcca 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -159,7 +159,7 @@
<string name="tts_default_pitch_title" msgid="6135942113172488671">"ஒலித்திறன்"</string>
<string name="tts_default_pitch_summary" msgid="1944885882882650009">"உருவாக்கப்படும் பேச்சின் டோன் பாதிக்கப்படும்"</string>
<string name="tts_default_lang_title" msgid="8018087612299820556">"மொழி"</string>
- <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியில்"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியைப் பயன்படுத்தவும்"</string>
<string name="tts_lang_not_selected" msgid="7395787019276734765">"மொழி தேர்ந்தெடுக்கப்படவில்லை"</string>
<string name="tts_default_lang_summary" msgid="5219362163902707785">"பேசப்படும் உரைக்கு மொழி சார்ந்த குரலை அமைக்கிறது"</string>
<string name="tts_play_example_title" msgid="7094780383253097230">"எடுத்துக்காட்டைக் கவனிக்கவும்"</string>
@@ -264,7 +264,7 @@
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"வைஃபை இயங்கும் போதும் (வேகமான நெட்வொர்க் மாற்றத்திற்கு), மொபைல் டேட்டாவை எப்போதும் இயக்கத்தில் வைக்கும்."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை கிடைக்கும் போது, அதைப் பயன்படுத்தும்"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB பிழைதிருத்தத்தை அனுமதிக்கவா?"</string>
- <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைதிருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் பயன்பாடுகளை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string>
+ <string name="adb_warning_message" msgid="7316799925425402244">"USB பிழைதிருத்தம் மேம்படுத்தல் நோக்கங்களுக்காக மட்டுமே. அதை உங்கள் கணினி மற்றும் சாதனத்திற்கு இடையில் தரவை நகலெடுக்கவும், அறிவிப்பு இல்லாமல் உங்கள் சாதனத்தில் ஆப்ஸை நிறுவவும், பதிவு தரவைப் படிக்கவும் பயன்படுத்தவும்."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"நீங்கள் ஏற்கனவே அனுமதித்த எல்லா கணினிகளிலிருந்தும் USB பிழைத்திருத்தத்திற்கான அணுகலைத் திரும்பப்பெற வேண்டுமா?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"மேம்பட்ட அமைப்புகளை அனுமதிக்கவா?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"இந்த அமைப்பு மேம்பட்டப் பயன்பாட்டிற்காக மட்டுமே. உங்கள் சாதனம் மற்றும் அதில் உள்ள பயன்பாடுகளைச் சிதைக்கும் அல்லது தவறாகச் செயல்படும் வகையில் பாதிப்பை ஏற்படுத்தும்."</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 83636dc19beb..e591121d4b59 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -143,7 +143,7 @@
<string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్‌డేట్‌లు"</string>
<string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్‌స్పాట్"</string>
- <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టెథెరింగ్"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string>
<string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</string>
<string name="managed_user_title" msgid="8109605045406748842">"అన్ని కార్యాలయ అనువర్తనాలు"</string>
@@ -376,7 +376,7 @@
<string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"రంగు సవరణ"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ ఫీచ‌ర్‌ ప్రయోగాత్మకమైనది, పనితీరుపై ప్రభావం చూపవచ్చు."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
<string name="power_remaining_settings_home_page" msgid="4845022416859002011">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="6123167166221295462">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"నిలిపివేయబడింది"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"అనుమతించినవి"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"అనుమతించబడలేదు"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయండి"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడం"</string>
<string name="home" msgid="3256884684164448244">"సెట్టింగ్‌ల హోమ్"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 5dca4759a629..0d3e8de0559d 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -201,7 +201,7 @@
<string name="vpn_settings_not_available" msgid="956841430176985598">"Ushbu foydalanuvchi uchun VPN sozlamalari mavjud emas"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"Ushbu foydalanuvchi uchun Modem rejimi sozlamalari mavjud emas"</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"Ushbu foydalanuvchi uchun Internetga kirish nuqtasi (APN) sozlamalari mavjud emas"</string>
- <string name="enable_adb" msgid="7982306934419797485">"USB orqali nosozliklarni tuzatish"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"USB orqali nosozliklarni aniqlash"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"USB orqali kompyuterga ulanganda tuzatish rejimi yoqilsin"</string>
<string name="clear_adb_keys" msgid="4038889221503122743">"USB orqali nosozliklarni tuzatishni taqiqlash"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Xatoliklar hisoboti"</string>
@@ -264,7 +264,7 @@
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobil internet har doim yoniq tursin, hatto Wi-Fi yoniq bo‘lsa ham (bir tarmoqdan ikkinchisiga tezroq o‘tish uchun)."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Modem rejimida apparatli tezlashtirishdan foydalanish (agar mavjud bo‘lsa)"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"USB orqali nosozliklarni tuzatishga ruxsat berilsinmi?"</string>
- <string name="adb_warning_message" msgid="7316799925425402244">"USB orqali nosozliklarni tuzatish faqat dasturlash maqsadlarida yoqiladi. Undan ma‘lumotlarni qurilmangiz va kompyuter o‘rtasida ko‘chirish, ilovalarni xabarnomasiz o‘rnatish va jurnal ma‘lumotlarini o‘qish uchun foydalaniladi."</string>
+ <string name="adb_warning_message" msgid="7316799925425402244">"USB orqali nosozliklarni aniqlash faqat dasturlash maqsadlarida yoqiladi. Undan maʼlumotlarni qurilmangiz va kompyuter o‘rtasida ko‘chirish, ilovalarni xabarnomasiz o‘rnatish va jurnal maʼlumotlarini o‘qish uchun foydalaniladi."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"USB orqali nosozliklarni tuzatishga berilgan ruxsat siz hisobingizga kirgan barcha kompyuterlar uchun bekor qilinsinmi?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Dasturlash sozlamalariga ruxsat berilsinmi?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"Bu sozlamalar faqat dasturlash maqsadlariga mo‘ljallangan. Shuning uchun, ular qurilmangizga va undagi ilovalariga shikast yetkazib, noto‘g‘ri ishlashiga sabab bo‘lishi mumkin."</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 4e2d6fa76ad0..5c06c80a049c 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -201,7 +201,7 @@
<string name="vpn_settings_not_available" msgid="956841430176985598">"Cài đặt VPN không khả dụng cho người dùng này"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"Cài đặt chia sẻ kết nối không khả dụng cho người dùng này"</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"Cài đặt tên điểm truy cập không khả dụng cho người dùng này"</string>
- <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi USB"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi qua USB"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
<string name="clear_adb_keys" msgid="4038889221503122743">"Thu hồi ủy quyền gỡ lỗi USB"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Phím tắt báo cáo lỗi"</string>
@@ -263,7 +263,7 @@
<string name="debug_view_attributes" msgid="6485448367803310384">"Cho phép kiểm tra thuộc tính của chế độ xem"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Luôn bật dữ liệu di động ngay cả khi Wi-Fi đang hoạt động (để chuyển đổi mạng nhanh)."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Sử dụng tính năng tăng tốc phần cứng khi chia sẻ kết nối nếu có"</string>
- <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi USB?"</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi qua USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
<string name="adb_keys_warning_message" msgid="5659849457135841625">"Thu hồi quyền truy cập gỡ lỗi USB từ tất cả máy tính mà bạn đã ủy quyền trước đó?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Cho phép cài đặt phát triển?"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e85199f9ca0d..c64130210d37 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -329,7 +329,7 @@
<string name="show_all_anrs" msgid="4924885492787069007">"顯示背景 ANR"</string>
<string name="show_all_anrs_summary" msgid="6636514318275139826">"為背景應用程式顯示「應用程式無回應」對話方塊"</string>
<string name="show_notification_channel_warnings" msgid="1399948193466922683">"顯示通知管道警告"</string>
- <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發佈通知時,在畫面上顯示警告"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發布通知時,在畫面上顯示警告"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"強制允許將應用程式寫入外部儲存空間"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 84f5a04c6c25..98db7c8fb59c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -27,6 +27,7 @@ import android.content.SharedPreferences;
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -799,10 +800,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
== BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ||
mDevice.getBluetoothClass().getDeviceClass()
== BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
- mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
- } else {
- mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+ EventLog.writeEvent(0x534e4554, "138529441", -1, "");
}
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
}
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 9be636d69daa..4a0ed6f91ed3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -148,5 +148,6 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 5));
VALIDATORS.put(
Global.POWER_BUTTON_VERY_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 1));
+ VALIDATORS.put(Global.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 32fc7ff978cd..720266a72423 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1521,14 +1521,14 @@ public class SettingsProvider extends ContentProvider {
return false;
}
- // Special cases for location providers (sigh).
- if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
- return updateLocationProvidersAllowedLocked(value, tag, owningUserId, makeDefault,
- forceNotify);
- }
-
// Mutate the value.
synchronized (mLock) {
+ // Special cases for location providers (sigh).
+ if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
+ return updateLocationProvidersAllowedLocked(value, tag, owningUserId, makeDefault,
+ forceNotify);
+ }
+
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index d7eb7e955d8c..6ea3db366f66 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -308,7 +308,6 @@ public class SettingsBackupTest {
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
- Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index fc6397dc2013..96671c8c0175 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -25,9 +25,9 @@
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"系統即將在手機上顯示錯誤報告"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"選取即可分享錯誤報告"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"輕觸即可分享錯誤報告"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕擷取畫面的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"錯誤報告的資料來自系統的各種記錄檔,當中可能包含敏感資料 (例如應用程式使用情形和位置資料)。請務必只與你信任的使用者和應用程式分享錯誤報告。"</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"不要再顯示"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
@@ -35,9 +35,9 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"無法在 ZIP 檔案中加入錯誤報告"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"未命名"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"詳細資料"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕擷取畫面"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕擷取畫面。"</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕擷取畫面。"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕截圖"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕截圖。"</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕截圖。"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"錯誤報告 <xliff:g id="ID">#%d</xliff:g> 的詳細資料"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"檔案名稱"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"錯誤標題"</string>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 4ba5146b8d39..d2f168eb5e3e 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -52,7 +52,6 @@ import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import java.io.IOException;
-import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -154,10 +153,7 @@ public final class RingtonePickerActivity extends AlertActivity implements
if (which == mCursor.getCount() + mStaticItemCount) {
// The "Add new ringtone" item was clicked. Start a file picker intent to select
// only audio files (MIME type "audio/*")
- final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
- chooseFile.setType("audio/*");
- chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
- new String[] { "audio/*", "application/ogg" });
+ final Intent chooseFile = getMediaFilePickerIntent();
startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
return;
}
@@ -375,7 +371,8 @@ public final class RingtonePickerActivity extends AlertActivity implements
setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
}
// If external storage is available, add a button to install sounds from storage.
- if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ if (resolvesMediaFilePicker()
+ && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
addNewSoundItem(listView);
}
@@ -633,6 +630,18 @@ public final class RingtonePickerActivity extends AlertActivity implements
return ringtoneManagerPos + mStaticItemCount;
}
+ private Intent getMediaFilePickerIntent() {
+ final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.setType("audio/*");
+ chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+ new String[] { "audio/*", "application/ogg" });
+ return chooseFile;
+ }
+
+ private boolean resolvesMediaFilePicker() {
+ return getMediaFilePickerIntent().resolveActivity(getPackageManager()) != null;
+ }
+
private static class LocalizedCursor extends CursorWrapper {
final int mTitleIndex;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4f74605b4003..403e894a68e4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -609,6 +609,10 @@
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:process=":ui"
android:visibleToInstantApps="true">
+ <intent-filter>
+ <action android:name="android.intent.action.CHOOSER" />
+ <category android:name="android.intent.category.VOICE" />
+ </intent-filter>
</activity>
<!-- Doze with notifications, run in main sysui process for every user -->
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 834f4fc75dce..db026cad6ef5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,6 +14,9 @@
package com.android.systemui.plugins.qs;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
@@ -25,6 +28,7 @@ import com.android.systemui.plugins.qs.QSTile.Callback;
import com.android.systemui.plugins.qs.QSTile.Icon;
import com.android.systemui.plugins.qs.QSTile.State;
+import java.lang.annotation.Retention;
import java.util.Objects;
import java.util.function.Supplier;
@@ -64,6 +68,14 @@ public interface QSTile {
void destroy();
+ /**
+ * return true if the tile supports detail views, and not
+ * only boolean states
+ */
+ default boolean supportsDetailView() {
+ return false;
+ }
+
CharSequence getTileLabel();
State getState();
@@ -72,6 +84,17 @@ public interface QSTile {
return logMaker;
}
+ @Retention(SOURCE)
+ @IntDef({COLOR_TILE_ACCENT, COLOR_TILE_RED, COLOR_TILE_BLUE, COLOR_TILE_YELLOW,
+ COLOR_TILE_GREEN})
+ @interface ColorTile {}
+ int COLOR_TILE_ACCENT = 0;
+ int COLOR_TILE_RED = 1;
+ int COLOR_TILE_BLUE = 2;
+ int COLOR_TILE_YELLOW = 3;
+ int COLOR_TILE_GREEN = 4;
+ default void setColor(@ColorTile int color) {}
+
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;
@@ -118,6 +141,7 @@ public interface QSTile {
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
+ public int colorActive = -1;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -137,7 +161,8 @@ public interface QSTile {
|| !Objects.equals(other.dualTarget, dualTarget)
|| !Objects.equals(other.slash, slash)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
- || !Objects.equals(other.showRippleEffect, showRippleEffect);
+ || !Objects.equals(other.showRippleEffect, showRippleEffect)
+ || !Objects.equals(other.colorActive, colorActive);
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
@@ -152,6 +177,7 @@ public interface QSTile {
other.slash = slash != null ? slash.copy() : null;
other.handlesLongClick = handlesLongClick;
other.showRippleEffect = showRippleEffect;
+ other.colorActive = colorActive;
return changed;
}
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 0d88a2074efa..3adc2792ef10 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -41,7 +41,7 @@
<string name="keyguard_low_battery" msgid="9218432555787624490">"Připojte dobíjecí zařízení."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"Klávesy odemknete stisknutím tlačítka nabídky."</string>
<string name="keyguard_network_locked_message" msgid="6743537524631420759">"Síť je blokována"</string>
- <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Není vložena SIM karta"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Chybí SIM karta"</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"V tabletu není SIM karta."</string>
<string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"V telefonu není SIM karta."</string>
<string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Vložte SIM kartu."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 22c4c48a7e37..9c9de22b96d9 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -37,7 +37,7 @@
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ بی‌سیم"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string>
- <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ آهسته"</string>
+ <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهسته‌آهسته شارژ می‌شود"</string>
<string name="keyguard_low_battery" msgid="9218432555787624490">"شارژر را وصل کنید."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"برای باز کردن قفل روی «منو» فشار دهید."</string>
<string name="keyguard_network_locked_message" msgid="6743537524631420759">"شبکه قفل شد"</string>
diff --git a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
deleted file mode 100644
index 0c6d57dd6183..000000000000
--- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:topLeftRadius="@dimen/biometric_dialog_corner_size"
- android:topRightRadius="@dimen/biometric_dialog_corner_size"
- android:bottomLeftRadius="@dimen/biometric_dialog_corner_size"
- android:bottomRightRadius="@dimen/biometric_dialog_corner_size"/>
-</shape>
diff --git a/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml b/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml
new file mode 100644
index 000000000000..ce53e278302a
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<com.android.systemui.biometrics.AuthBiometricFingerprintView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/contents"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/auth_biometric_contents"/>
+
+</com.android.systemui.biometrics.AuthBiometricFingerprintView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index d83776b74fd1..23199aacc093 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -23,7 +23,8 @@
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/biometric_dialog_dim_color"/>
+ android:background="@color/biometric_dialog_dim_color"
+ android:contentDescription="@string/biometric_dialog_empty_space_description"/>
<View
android:id="@+id/panel"
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
deleted file mode 100644
index e687cdfac356..000000000000
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="bottom"
- android:background="@color/biometric_dialog_dim_color"
- android:orientation="vertical">
-
- <!-- This is not a Space since Spaces cannot be clicked -->
- <View
- android:id="@+id/space"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
- depending on horizontal/portrait orientation -->
- <View
- android:id="@+id/left_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- <LinearLayout
- android:id="@+id/dialog"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/biometric_dialog_bg"
- android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
- android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
- android:layout_marginRight="@dimen/biometric_dialog_border_padding">
-
- <TextView
- android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:layout_marginTop="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="20sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:paddingTop="8dp"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <ImageView
- android:id="@+id/biometric_icon"
- android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="48dp"
- android:scaleType="fitXY" />
-
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="24dp"
- android:layout_marginStart="24dp"
- android:paddingTop="16dp"
- android:paddingBottom="24dp"
- android:textSize="12sp"
- android:gravity="center_horizontal"
- android:accessibilityLiveRegion="polite"
- android:textColor="@color/biometric_dialog_gray"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="72dip"
- android:paddingTop="24dp"
- android:layout_gravity="center_vertical"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:measureWithLargestChild="true">
- <Space android:id="@+id/leftSpacer"
- android:layout_width="12dp"
- android:layout_height="match_parent"
- android:visibility="visible" />
- <!-- Negative Button -->
- <Button android:id="@+id/button2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:gravity="center"
- android:maxLines="2" />
- <Space android:id="@+id/middleSpacer"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="visible" />
- <!-- Positive Button -->
- <Button android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="gone"/>
- <!-- Try Again Button -->
- <Button android:id="@+id/button_try_again"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="gone"/>
- <Space android:id="@+id/rightSpacer"
- android:layout_width="12dip"
- android:layout_height="match_parent"
- android:visibility="visible" />
- </LinearLayout>
- </LinearLayout>
-
- <!-- This is not a Space since Spaces cannot be clicked. The width of this changes
- depending on horizontal/portrait orientation -->
- <View
- android:id="@+id/right_space"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:contentDescription="@string/biometric_dialog_empty_space_description"/>
-
- </LinearLayout>
-
- </ScrollView>
-
- </LinearLayout>
-
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
index 2b2100c850d8..9376b1f04cc8 100644
--- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
+++ b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
@@ -1,17 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_enterprise_disclosure"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingStart="@dimen/keyguard_indication_text_padding"
- android:paddingEnd="@dimen/keyguard_indication_text_padding"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:visibility="gone"/>
-
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
android:id="@+id/keyguard_indication_text"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index 36f382b19f4a..56efb4911cd0 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -25,6 +25,17 @@
android:orientation="horizontal">
+ <com.android.systemui.util.AutoMarqueeTextView
+ android:id="@+id/no_carrier_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ android:textDirection="locale"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:singleLine="true"
+ android:maxEms="7"
+ android:visibility="gone"/>
+
<include
layout="@layout/qs_carrier"
android:id="@+id/carrier1"
diff --git a/packages/SystemUI/res/layout/qs_tile_detail_text.xml b/packages/SystemUI/res/layout/qs_tile_detail_text.xml
new file mode 100644
index 000000000000..bcbf826b9d49
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_detail_text.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<!-- use 'dp' instead of 'sp' as we do not want the text to increase
+ if the user scales the font size -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|center_horizontal"
+ android:text="..."
+ android:textSize="16dp"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:singleLine="true"
+ android:visibility="gone"
+ android:paddingBottom="@dimen/qs_tile_detail_padding"
+ android:clickable="false"
+ android:focusable="false" />
+
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index dcd31bda79b3..36745648b051 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -329,7 +329,7 @@
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Вызначэнне месцазнаходжання адключана"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"Мультымедыйная прылада"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Толькі экстраныя выклікі"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Толькі экстранныя выклікі"</string>
<string name="quick_settings_settings_label" msgid="5326556592578065401">"Налады"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"Час"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"Я"</string>
@@ -423,7 +423,7 @@
<string name="keyguard_indication_charging_time_wireless" msgid="6959284458466962592">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе бесправадная зарадка (да поўнага зараду засталося <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
<string name="keyguard_indication_charging_time" msgid="2056340799276374421">"Ідзе зарадка (<xliff:g id="PERCENTAGE">%2$s</xliff:g>, яшчэ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
<string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"Ідзе хуткая зарадка (<xliff:g id="PERCENTAGE">%2$s</xliff:g>, яшчэ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"Ідзе павольная зарадка (<xliff:g id="PERCENTAGE">%2$s</xliff:g>, яшчэ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"Ідзе павольная зарадка (<xliff:g id="PERCENTAGE">%2$s</xliff:g>, <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> да канца)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Перайсці да іншага карыстальніка, бягучы карыстальнік <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Бягучы карыстальнік <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5141b57ec295..6b7561bdcd3b 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -232,9 +232,9 @@
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"বিমান মোড চালু হয়েছে।"</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"সম্পূর্ণ নীরব"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"শুধুমাত্র অ্যালার্ম"</string>
- <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"বিরক্ত করবেন না।"</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\'বিরক্ত করবেন না\' বন্ধ আছে।"</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"\'বিরক্ত করবেন না\' চালু করা হয়েছে।"</string>
+ <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"বিরক্ত করবে না।"</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\'বিরক্ত করবে না\' বন্ধ আছে।"</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"\'বিরক্ত করবে না\' চালু করা হয়েছে।"</string>
<string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"ব্লুটুথ"</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"ব্লুটুথ বন্ধ আছে।"</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"ব্লুটুথ চালু আছে।"</string>
@@ -299,7 +299,7 @@
<string name="start_dreams" msgid="5640361424498338327">"স্ক্রিন সেভার"</string>
<string name="ethernet_label" msgid="7967563676324087464">"ইথারনেট"</string>
<string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"আরও বিকল্পের জন্য আইকনগুলি টাচ করে ধরে থাকুন"</string>
- <string name="quick_settings_dnd_label" msgid="7112342227663678739">"বিরক্ত করবেন না"</string>
+ <string name="quick_settings_dnd_label" msgid="7112342227663678739">"বিরক্ত করবে না"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"শুধুমাত্র অগ্রাধিকার"</string>
<string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"শুধুমাত্র অ্যালার্মগুলি"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"একদম নিরব"</string>
@@ -461,7 +461,7 @@
<string name="manage_notifications_text" msgid="2386728145475108753">"পরিচালনা করুন"</string>
<string name="notification_section_header_gentle" msgid="4372438504154095677">"নীরব বিজ্ঞপ্তি"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"সব নীরব বিজ্ঞপ্তি মুছুন"</string>
- <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'বিরক্ত করবেন না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
+ <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'বিরক্ত করবে না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"এখন শুরু করুন"</string>
<string name="empty_shade_text" msgid="708135716272867002">"কোনো বিজ্ঞপ্তি নেই"</string>
<string name="profile_owned_footer" msgid="8021888108553696069">"প্রোফাইল পর্যবেক্ষণ করা হতে পারে"</string>
@@ -737,9 +737,9 @@
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ক্যালেন্ডার"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ভলিউম নিয়ন্ত্রণ সহ দেখান"</string>
- <string name="volume_and_do_not_disturb" msgid="1750270820297253561">"বিরক্ত করবেন না"</string>
+ <string name="volume_and_do_not_disturb" msgid="1750270820297253561">"বিরক্ত করবে না"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ভলিউম বোতামের শর্টকাট"</string>
- <string name="volume_up_silent" msgid="7545869833038212815">"ভলিউম বাড়িয়ে \'বিরক্ত করবেন না\' মোড থেকে বেরিয়ে আসুন"</string>
+ <string name="volume_up_silent" msgid="7545869833038212815">"ভলিউম বাড়িয়ে \'বিরক্ত করবে না\' মোড থেকে বেরিয়ে আসুন"</string>
<string name="battery" msgid="7498329822413202973">"ব্যাটারি"</string>
<string name="clock" msgid="7416090374234785905">"ঘড়ি"</string>
<string name="headset" msgid="4534219457597457353">"হেডসেট"</string>
@@ -883,10 +883,10 @@
<string name="mobile_carrier_text_format" msgid="3241721038678469804">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ওয়াই ফাই বন্ধ আছে"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ বন্ধ আছে"</string>
- <string name="dnd_is_off" msgid="6167780215212497572">"বিরক্ত করবেন না বিকল্পটি বন্ধ আছে"</string>
- <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"বিরক্ত করবেন না বিকল্পটি একটি স্বয়ংক্রিয় নিয়ম <xliff:g id="ID_1">%s</xliff:g> এর দ্বারা চালু করা হয়েছে।"</string>
- <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"বিরক্ত করবেন না বিকল্পটি একটি অ্যাপ <xliff:g id="ID_1">%s</xliff:g> এর দ্বারা চালু করা হয়েছে।"</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"বিরক্ত করবেন না বিকল্পটি একটি স্বয়ংক্রিয় নিয়ম বা অ্যাপের দ্বারা চালু করা হয়েছে।"</string>
+ <string name="dnd_is_off" msgid="6167780215212497572">"বিরক্ত করবে না বিকল্পটি বন্ধ আছে"</string>
+ <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"বিরক্ত করবে না বিকল্পটি একটি স্বয়ংক্রিয় নিয়ম <xliff:g id="ID_1">%s</xliff:g> এর দ্বারা চালু করা হয়েছে।"</string>
+ <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"বিরক্ত করবে না বিকল্পটি একটি অ্যাপ <xliff:g id="ID_1">%s</xliff:g> এর দ্বারা চালু করা হয়েছে।"</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"বিরক্ত করবে না বিকল্পটি একটি স্বয়ংক্রিয় নিয়ম বা অ্যাপের দ্বারা চালু করা হয়েছে।"</string>
<string name="qs_dnd_until" msgid="3469471136280079874">"<xliff:g id="ID_1">%s</xliff:g> পর্যন্ত"</string>
<string name="qs_dnd_keep" msgid="1825009164681928736">"রাখুন"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"বদলে দিন"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index abea52a0adf8..87aaa94d1dd3 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -194,7 +194,7 @@
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Sdílené připojení přes Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Režim Letadlo."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN je zapnuto."</string>
- <string name="accessibility_no_sims" msgid="3957997018324995781">"Není vložena SIM karta"</string>
+ <string name="accessibility_no_sims" msgid="3957997018324995781">"Chybí SIM karta"</string>
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Probíhá změna sítě operátora"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Otevřít podrobnosti o baterii"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Stav baterie: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 6a97ee6e9352..57c934e5657d 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -215,7 +215,7 @@
<skip />
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifikationen er annulleret."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notifikationspanel."</string>
- <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hurtige indstillinger."</string>
+ <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Kvikmenu."</string>
<string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Låseskærm."</string>
<string name="accessibility_desc_settings" msgid="3417884241751434521">"Indstillinger"</string>
<string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Oversigt."</string>
@@ -588,7 +588,7 @@
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Vis procent for det indbyggede batteri"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Vis procenttallet for batteriniveauet i ikonet for statusbjælken, når der ikke oplades"</string>
- <string name="quick_settings" msgid="10042998191725428">"Hurtige indstillinger"</string>
+ <string name="quick_settings" msgid="10042998191725428">"Kvikmenu"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusbjælke"</string>
<string name="overview" msgid="4018602013895926956">"Oversigt"</string>
<string name="demo_mode" msgid="2532177350215638026">"Demotilstand for systemets brugerflade"</string>
@@ -604,7 +604,7 @@
<string name="zen_alarm_warning" msgid="444533119582244293">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3980063409350522735">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="4242179982586714810">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
- <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Hurtige indstillinger <xliff:g id="TITLE">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Kvikmenu <xliff:g id="TITLE">%s</xliff:g>."</string>
<string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Hotspot"</string>
<string name="accessibility_managed_profile" msgid="6613641363112584120">"Arbejdsprofil"</string>
<string name="tuner_warning_title" msgid="7094689930793031682">"Sjovt for nogle, men ikke for alle"</string>
@@ -617,8 +617,8 @@
<string name="activity_not_found" msgid="348423244327799974">"Applikationen er ikke installeret på din enhed."</string>
<string name="clock_seconds" msgid="7689554147579179507">"Vis sekunder"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statuslinjen. Dette kan påvirke batteriets levetid."</string>
- <string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Hurtige indstillinger"</string>
- <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Hurtige indstillinger"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Kvikmenu"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Kvikmenu"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentel"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå Bluetooth til?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
@@ -813,15 +813,15 @@
<string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add" msgid="3520406665865985109">"Føj <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigeringsværktøj for Hurtige indstillinger."</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigeringsværktøj til Kvikmenu."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="dock_forced_resizable" msgid="5914261505436217520">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"Appen understøtter ikke opdelt skærm."</string>
<string name="forced_resizable_secondary_display" msgid="4230857851756391925">"Appen fungerer muligvis ikke på sekundære skærme."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"Appen kan ikke åbnes på sekundære skærme."</string>
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Åbn Indstillinger."</string>
- <string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Åbn Hurtige indstillinger."</string>
- <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Luk Hurtige indstillinger."</string>
+ <string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Åbn Kvikmenu."</string>
+ <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Luk Kvikmenu."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Alarmen er indstillet."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget ind som <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="data_connection_no_internet" msgid="4503302451650972989">"Intet internet"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 9903cc04e97a..938cb0dd3b12 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -401,6 +401,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"Swipe up to try again"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string>
@@ -544,6 +545,7 @@
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"This keeps it in view until you unpin. Touch &amp; hold Home to unpin."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"To unpin this screen, touch &amp; hold Back and Overview buttons"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch &amp; hold Back and Home buttons"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"To unpin this screen, swipe up &amp; hold"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
<string name="screen_pinning_start" msgid="1022122128489278317">"Screen pinned"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index d8c862affe08..139215aa196f 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -401,6 +401,7 @@
<string name="speed_bump_explanation" msgid="1288875699658819755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎Less urgent notifications below‎‏‎‎‏‎"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‎Tap again to open‎‏‎‎‏‎"</string>
<string name="keyguard_unlock" msgid="6035822649218712063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎Swipe up to open‎‏‎‎‏‎"</string>
+ <string name="keyguard_retry" msgid="5221600879614948709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‏‎Swipe up to try again‎‏‎‎‏‎"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎This device is managed by your organization‎‏‎‎‏‎"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎This device is managed by ‎‏‎‎‏‏‎<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="phone_hint" msgid="4872890986869209950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎Swipe from icon for phone‎‏‎‎‏‎"</string>
@@ -544,6 +545,7 @@
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎This keeps it in view until you unpin. Touch &amp; hold Home to unpin.‎‏‎‎‏‎"</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎To unpin this screen, touch &amp; hold Back and Overview buttons‎‏‎‎‏‎"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎To unpin this screen, touch &amp; hold Back and Home buttons‎‏‎‎‏‎"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎To unpin this screen, swipe up &amp; hold‎‏‎‎‏‎"</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎Got it‎‏‎‎‏‎"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎No thanks‎‏‎‎‏‎"</string>
<string name="screen_pinning_start" msgid="1022122128489278317">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎Screen pinned‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 137b780fee2a..e8e4232063e3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -417,7 +417,7 @@
<string name="keyguard_indication_charging_time_wireless" msgid="6959284458466962592">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • En recharge sans fil (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> jusqu\'à la recharge complète)"</string>
<string name="keyguard_indication_charging_time" msgid="2056340799276374421">"En recharge : <xliff:g id="PERCENTAGE">%2$s</xliff:g> (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> jusqu\'à charge complète)"</string>
<string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"En recharge rapide : <xliff:g id="PERCENTAGE">%2$s</xliff:g> (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> jusqu\'à ch. comp.)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"En recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> jusqu\'à ch. comp.)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"Recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> (à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 15d9c2b6167a..5480eab8cf65 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -599,7 +599,7 @@
<string name="status_bar_work" msgid="6022553324802866373">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="7057575501472249002">"Modo avión"</string>
<string name="add_tile" msgid="2995389510240786221">"Engade un atallo"</string>
- <string name="broadcast_tile" msgid="3894036511763289383">"Atallo de emisión"</string>
+ <string name="broadcast_tile" msgid="3894036511763289383">"Atallo de difusión"</string>
<string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g> a menos que desactives esta opción antes desa hora"</string>
<string name="zen_alarm_warning" msgid="444533119582244293">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3980063409350522735">"ás <xliff:g id="WHEN">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f90f494fe3a2..1a15cf705d91 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -314,7 +314,7 @@
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="4930931771490695395">"શ્રવણ યંત્રો"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"ચાલુ કરી રહ્યાં છીએ…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"તેજ"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"આપમેળે ફેરવો"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ઑટો રોટેટ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"સ્ક્રીનને આપમેળે ફેરવો"</string>
<string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> મોડ"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"પરિભ્રમણ લૉક થયું"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 5f47e20e7466..3e3696b45ad9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -647,7 +647,7 @@
<string name="inline_minimize_button" msgid="966233327974702195">"Kis méret"</string>
<string name="inline_silent_button_silent" msgid="5315879183296940969">"Néma"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Néma megjelenítés"</string>
- <string name="inline_silent_button_alert" msgid="6008435419895088034">"Értesítések"</string>
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Figyelemfelkeltő"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Értesítések folytatása"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Az értesítések kikapcsolása"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Továbbra is megjelenjenek az alkalmazás értesítései?"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 7b55d18676da..0febc8ed4262 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -83,4 +83,9 @@
<color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal -->
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
+ <color name="GM2_green_500">#FF41Af6A</color>
+ <color name="GM2_blue_500">#5195EA</color>
+ <color name="GM2_red_500">#E25142</color>
+ <color name="GM2_yellow_500">#F5C518</color>
+
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 3760007b2291..e9b150c1a66e 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -250,9 +250,9 @@
<string name="accessibility_quick_settings_close" msgid="3115847794692516306">"ପ୍ୟାନେଲ୍ ବନ୍ଦ କରନ୍ତୁ।"</string>
<string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"ଅଧିକ ସମୟ।"</string>
<string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"କମ୍ ସମୟ।"</string>
- <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"ଫ୍ଲାଶ୍‌ଲାଇଟ୍ ଅଫ୍ ଅଛି।"</string>
+ <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"ଫ୍ଲାସ୍‍ଲାଇଟ୍ ବନ୍ଦ ଅଛି।"</string>
<string name="accessibility_quick_settings_flashlight_unavailable" msgid="8012811023312280810">"ଟର୍ଚ୍ଚ ଲାଇଟ୍‍ ଅନୁପଲବ୍ଧ।"</string>
- <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"ଫ୍ଲାଶ୍‌ଲାଇଟ୍ ଅନ୍ ଅଛି।"</string>
+ <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"ଫ୍ଲାସ୍‍ଲାଇଟ୍ ଚାଲୁଅଛି।"</string>
<string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ବନ୍ଦ ଅଛି।"</string>
<string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ଅନ୍ ଅଛି।"</string>
<string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"ରଙ୍ଗ ବିପରୀତିକରଣକୁ ବନ୍ଦ କରିଦିଆଗଲା।"</string>
@@ -362,7 +362,7 @@
<item quantity="one">%d ଡିଭାଇସ୍</item>
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ବିଜ୍ଞପ୍ତି"</string>
- <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ଫ୍ଲାଶ୍‍ଲାଇଟ"</string>
+ <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ଫ୍ଲାସ୍‍ଲାଇଟ୍"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ଡାଟାର ବ୍ୟବହାର"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ଅବଶିଷ୍ଟ ଡାଟା"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 93a2c124782a..4be1a0f3aa17 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -465,10 +465,10 @@
<string name="media_projection_dialog_service_text" msgid="3075544489835858258">"Podczas nagrywania lub przesyłania usługa udostępniająca tę funkcję może rejestrować wszelkie informacje poufne wyświetlane na ekranie lub odtwarzane na urządzeniu takie jak dźwięki czy podawane hasła, informacje o płatnościach, zdjęcia i wiadomości."</string>
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Ujawnianie poufnych informacji podczas przesyłania/nagrywania"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nie pokazuj ponownie"</string>
- <string name="clear_all_notifications_text" msgid="814192889771462828">"Ukryj wszystkie"</string>
+ <string name="clear_all_notifications_text" msgid="814192889771462828">"Usuń wszystkie"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Zarządzaj"</string>
- <string name="notification_section_header_gentle" msgid="4372438504154095677">"Powiadomienia ciche"</string>
- <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Wyczyść wszystkie ciche powiadomienia"</string>
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Ciche powiadomienia"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Usuń wszystkie ciche powiadomienia"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Powiadomienia wstrzymane przez tryb Nie przeszkadzać"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Rozpocznij teraz"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Brak powiadomień"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index d56ccefba43d..2b12149e06ad 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -845,7 +845,7 @@
<string name="pip_skip_to_prev" msgid="1955311326688637914">"முந்தையதற்குச் செல்"</string>
<string name="thermal_shutdown_title" msgid="4458304833443861111">"வெப்பத்தினால் ஃபோன் ஆஃப் செய்யப்பட்டது"</string>
<string name="thermal_shutdown_message" msgid="9006456746902370523">"இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது"</string>
- <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"உங்கள் ஃபோன் அதிகமாகச் சூடானதால், அதன் சூட்டைக் குறைக்க, ஆஃப் செய்யப்பட்டது. இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது.\n\nபின்வருவனவற்றைச் செய்தால், ஃபோன் சூடாகலாம்:\n • அதிகளவு தரவைப் பயன்படுத்தும் பயன்பாடுகளை (எ.கா: கேமிங், வீடியோ (அ) வழிகாட்டுதல் பயன்பாடுகள்) பயன்படுத்துவது\n • பெரிய கோப்புகளைப் பதிவிறக்குவது/பதிவேற்றுவது\n • அதிக வெப்பநிலையில் ஃபோனைப் பயன்படுத்துவது"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"உங்கள் ஃபோன் அதிகமாகச் சூடானதால், அதன் சூட்டைக் குறைக்க, ஆஃப் செய்யப்பட்டது. இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது.\n\nபின்வருவனவற்றைச் செய்தால், ஃபோன் சூடாகலாம்:\n • அதிகளவு தரவைப் பயன்படுத்தும் ஆப்ஸை (எ.கா: கேமிங், வீடியோ (அ) வழிகாட்டுதல் பயன்பாடுகள்) பயன்படுத்துவது\n • பெரிய கோப்புகளைப் பதிவிறக்குவது/பதிவேற்றுவது\n • அதிக வெப்பநிலையில் ஃபோனைப் பயன்படுத்துவது"</string>
<string name="high_temp_title" msgid="4589508026407318374">"மொபைல் சூடாகிறது"</string>
<string name="high_temp_notif_message" msgid="5642466103153429279">"மொபைலின் வெப்ப அளவு குறையும் போது, சில அம்சங்களைப் பயன்படுத்த முடியாது"</string>
<string name="high_temp_dialog_message" msgid="6840700639374113553">"உங்கள் மொபைலின் வெப்ப அளவு தானாகவே குறையும். தொடர்ந்து நீங்கள் மொபைலைப் பயன்படுத்தலாம், ஆனால் அதன் வேகம் குறைவாக இருக்கக்கூடும்.\n\nமொபைலின் வெப்ப அளவு குறைந்தவுடன், அது இயல்பு நிலையில் இயங்கும்."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6f38b18693d3..d3caa2b2c75d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -55,11 +55,11 @@
<string name="label_view" msgid="6304565553218192990">"Xem"</string>
<string name="always_use_device" msgid="4015357883336738417">"Luôn mở <xliff:g id="APPLICATION">%1$s</xliff:g> khi kết nối <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Luôn mở <xliff:g id="APPLICATION">%1$s</xliff:g> khi kết nối <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
- <string name="usb_debugging_title" msgid="4513918393387141949">"Cho phép gỡ lỗi USB?"</string>
+ <string name="usb_debugging_title" msgid="4513918393387141949">"Cho phép gỡ lỗi qua USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Tệp tham chiếu khóa RSA của máy tính là:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Luôn cho phép từ máy tính này"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Cho phép"</string>
- <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Tính năng gỡ lỗi USB không được phép"</string>
+ <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Không cho phép chế độ gỡ lỗi qua USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Người dùng hiện đã đăng nhập vào thiết bị này không thể bật tính năng gỡ lỗi USB. Để sử dụng tính năng này, hãy chuyển sang người dùng chính."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Đã tắt cổng USB"</string>
<string name="usb_contaminant_message" msgid="7379089091591609111">"Để bảo vệ thiết bị của bạn khỏi chất lỏng hay mảnh vỡ, cổng USB sẽ tắt và không phát hiện được bất kỳ phụ kiện nào.\n\nBạn sẽ nhận được thông báo khi có thể sử dụng lại cổng USB."</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 03e31cdc7ee6..457206dc9dc1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -264,8 +264,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"工作模式已開啟。"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"工作模式已關閉。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"工作模式已開啟。"</string>
- <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver 已關閉。"</string>
- <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver 已開啟。"</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"數據節省模式已關閉。"</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"數據節省模式已開啟。"</string>
<string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="5152819588955163090">"已關閉感應器隱私設定。"</string>
<string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="529705259565826355">"已開啟感應器隱私設定。"</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"螢幕亮度"</string>
@@ -459,8 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"不要再顯示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"管理"</string>
- <string name="notification_section_header_gentle" msgid="4372438504154095677">"無聲通知"</string>
- <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"清除所有無聲通知"</string>
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"靜音通知"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"清除所有靜音通知"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"「零打擾」模式已將通知設為暫停"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"立即開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"沒有通知"</string>
@@ -645,7 +645,7 @@
<string name="inline_block_button" msgid="8735843688021655065">"封鎖"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <string name="inline_silent_button_silent" msgid="5315879183296940969">"無聲"</string>
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"靜音"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"繼續顯示通知但不發出音效"</string>
<string name="inline_silent_button_alert" msgid="6008435419895088034">"快訊"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"繼續顯示通知"</string>
@@ -747,8 +747,8 @@
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"已與耳機連線"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"已與耳機連線"</string>
<string name="data_saver" msgid="5037565123367048522">"數據節省模式"</string>
- <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Data Saver 已開啟"</string>
- <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Data Saver 已關閉"</string>
+ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"數據節省模式已開啟"</string>
+ <string name="accessibility_data_saver_off" msgid="8841582529453005337">"數據節省模式已關閉"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"開啟"</string>
<string name="switch_bar_off" msgid="8803270596930432874">"關閉"</string>
<string name="nav_bar" msgid="1993221402773877607">"導覽列"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 61816f60d0ba..bda1c52f27c0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -204,4 +204,6 @@
<color name="GM2_yellow_500">#FFFBBC04</color>
<color name="GM2_green_500">#FF34A853</color>
+ <color name="GM2_blue_500">#FF4285F4</color>
+
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 38293bf2defd..61210d3e0011 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -480,4 +480,7 @@
<!-- Preferred refresh rate at keyguard, if supported by the display -->
<integer name="config_keyguardRefreshRate">-1</integer>
+ <!-- Whether or not to add a "people" notifications section -->
+ <bool name="config_usePeopleFiltering">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3a1f7a37729f..1079206c81ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -457,6 +457,7 @@
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
+ <dimen name="qs_tile_detail_padding">3dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 19e682b4b6a4..97e2f0f6562b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -294,7 +294,7 @@
<!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_try_again">Try again</string>
<!-- Content description for empty spaces that are not taken by the biometric dialog. Clicking on these areas will cancel authentication and dismiss the biometric dialog [CHAR LIMIT=NONE] -->
- <string name="biometric_dialog_empty_space_description">Empty region, tap to cancel authentication</string>
+ <string name="biometric_dialog_empty_space_description">Tap to cancel authentication</string>
<!-- Content description for the face icon when the device is not authenticating anymore [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_face_icon_description_idle">Please try again</string>
<!-- Content description for the face icon when the device is authenticating [CHAR LIMIT=NONE] -->
@@ -305,6 +305,8 @@
<string name="biometric_dialog_face_icon_description_confirmed">Confirmed</string>
<!-- Message shown when a biometric is authenticated, waiting for the user to confirm authentication [CHAR LIMIT=40]-->
<string name="biometric_dialog_tap_confirm">Tap Confirm to complete</string>
+ <!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_authenticated">Authenticated</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 3b03d67b4f83..e1b723e4d443 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -299,7 +299,9 @@ public class CarrierTextController {
}
}
}
- if (allSimsMissing) {
+ // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY
+ // This condition will also be true always when numSubs == 0
+ if (allSimsMissing && !anySimReadyAndInService) {
if (numSubs != 0) {
// Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
// This depends on mPlmn containing the text "Emergency calls only" when the radio
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b964edf7fc44..720074b0ad73 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -217,8 +217,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
- private static KeyguardUpdateMonitor sInstance;
-
private final Context mContext;
private final boolean mIsPrimaryUser;
HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
@@ -1389,15 +1387,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
Trace.endSection();
}
-
- /** Provides access to the static instance. */
- public static KeyguardUpdateMonitor getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new KeyguardUpdateMonitor(context, Looper.getMainLooper());
- }
- return sInstance;
- }
-
protected void handleStartedGoingToSleep(int arg1) {
checkIsHandlerThread();
mLockIconPressed = false;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
new file mode 100644
index 000000000000..0e079e36a175
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Extends the lifetime of foreground notification services such that they show for at least
+ * five seconds
+ */
+public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
+
+ private static final String TAG = "FGSLifetimeExtender";
+ @VisibleForTesting
+ static final int MIN_FGS_TIME_MS = 5000;
+
+ private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
+ private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public ForegroundServiceLifetimeExtender() {
+ }
+
+ @Override
+ public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
+ mNotificationSafeToRemoveCallback = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
+ if ((entry.notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
+ return false;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
+ }
+
+ @Override
+ public boolean shouldExtendLifetimeForPendingNotification(
+ @NonNull NotificationEntry entry) {
+ return shouldExtendLifetime(entry);
+ }
+
+ @Override
+ public void setShouldManageLifetime(
+ @NonNull NotificationEntry entry, boolean shouldManage) {
+ if (!shouldManage) {
+ mManagedEntries.remove(entry);
+ return;
+ }
+
+ mManagedEntries.add(entry);
+
+ Runnable r = () -> {
+ if (mManagedEntries.contains(entry)) {
+ mManagedEntries.remove(entry);
+ if (mNotificationSafeToRemoveCallback != null) {
+ mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
+ }
+ }
+ };
+ mHandler.postDelayed(r, MIN_FGS_TIME_MS);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 96b62ac918ab..f9d877142d21 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -50,12 +50,12 @@ public class ForegroundServiceNotificationListener {
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- addNotification(entry.notification, entry.importance);
+ addNotification(entry.notification, entry.getImportance());
}
@Override
public void onPostEntryUpdated(NotificationEntry entry) {
- updateNotification(entry.notification, entry.importance);
+ updateNotification(entry.notification, entry.getImportance());
}
@Override
@@ -66,6 +66,9 @@ public class ForegroundServiceNotificationListener {
removeNotification(entry.notification);
}
});
+
+ notificationEntryManager.addNotificationLifetimeExtender(
+ new ForegroundServiceLifetimeExtender());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index 0f99674e613f..3eb330e04381 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -38,6 +38,7 @@ import javax.inject.Inject;
*/
public class SystemUIAppComponentFactory extends AppComponentFactory {
+ private static final String TAG = "AppComponentFactory";
@Inject
public ContextComponentHelper mComponentHelper;
@@ -89,6 +90,12 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
public Service instantiateServiceCompat(
@NonNull ClassLoader cl, @NonNull String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ if (mComponentHelper == null) {
+ // This shouldn't happen, but does when a device is freshly formatted.
+ // Bug filed against framework to take a look: http://b/141008541
+ SystemUIFactory.getInstance().getRootComponent().inject(
+ SystemUIAppComponentFactory.this);
+ }
Service service = mComponentHelper.resolveService(className);
if (service != null) {
return service;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
index 33950144ac19..ff4eb83bd796 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
+import com.android.systemui.assist.AssistModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -34,7 +35,7 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module
+@Module(includes = {AssistModule.class})
public abstract class SystemUIModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 38ca70865a58..9bdfa03408aa 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -16,6 +16,8 @@
package com.android.systemui.assist;
+import static com.android.systemui.assist.AssistModule.ASSIST_HANDLE_THREAD_NAME;
+
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
@@ -28,20 +30,21 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.ScreenDecorations;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.NavigationModeController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
/**
* A class for managing Assistant handle logic.
@@ -49,6 +52,7 @@ import java.util.function.Supplier;
* Controls when visual handles for Assistant gesture affordance should be shown or hidden using an
* {@link AssistHandleBehavior}.
*/
+@Singleton
public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable {
private static final String TAG = "AssistHandleBehavior";
@@ -67,10 +71,9 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final Handler mHandler;
private final Runnable mHideHandles = this::hideHandles;
private final Runnable mShowAndGo = this::showAndGoInternal;
- private final Supplier<ScreenDecorations> mScreenDecorationsSupplier;
+ private final Provider<ScreenDecorations> mScreenDecorations;
private final PhenotypeHelper mPhenotypeHelper;
- private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap =
- new EnumMap<>(AssistHandleBehavior.class);
+ private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
private boolean mHandlesShowing = false;
private long mHandlesLastHiddenAt;
@@ -82,41 +85,25 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF;
private boolean mInGesturalMode;
- AssistHandleBehaviorController(Context context, AssistUtils assistUtils, Handler handler) {
- this(
- context,
- assistUtils,
- handler,
- () -> SysUiServiceProvider.getComponent(context, ScreenDecorations.class),
- new PhenotypeHelper(),
- /* testBehavior = */ null);
- }
-
- @VisibleForTesting
+ @Inject
AssistHandleBehaviorController(
Context context,
AssistUtils assistUtils,
- Handler handler,
- Supplier<ScreenDecorations> screenDecorationsSupplier,
+ @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
+ Provider<ScreenDecorations> screenDecorations,
PhenotypeHelper phenotypeHelper,
- @Nullable BehaviorController testBehavior) {
+ Map<AssistHandleBehavior, BehaviorController> behaviorMap,
+ NavigationModeController navigationModeController,
+ DumpController dumpController) {
mContext = context;
mAssistUtils = assistUtils;
mHandler = handler;
- mScreenDecorationsSupplier = screenDecorationsSupplier;
+ mScreenDecorations = screenDecorations;
mPhenotypeHelper = phenotypeHelper;
- mBehaviorMap.put(AssistHandleBehavior.OFF, new AssistHandleOffBehavior());
- mBehaviorMap.put(AssistHandleBehavior.LIKE_HOME, new AssistHandleLikeHomeBehavior());
- mBehaviorMap.put(
- AssistHandleBehavior.REMINDER_EXP,
- new AssistHandleReminderExpBehavior(handler, phenotypeHelper));
- if (testBehavior != null) {
- mBehaviorMap.put(AssistHandleBehavior.TEST, testBehavior);
- }
+ mBehaviorMap = behaviorMap;
mInGesturalMode = QuickStepContract.isGesturalMode(
- Dependency.get(NavigationModeController.class)
- .addListener(this::handleNavigationModeChange));
+ navigationModeController.addListener(this::handleNavigationModeChange));
setBehavior(getBehaviorMode());
mPhenotypeHelper.addOnPropertiesChangedListener(
@@ -128,7 +115,8 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null));
}
});
- Dependency.get(DumpController.class).registerDumpable(TAG, this);
+
+ dumpController.registerDumpable(TAG, this);
}
@Override // AssistHandleCallbacks
@@ -241,7 +229,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
}
if (handlesUnblocked(ignoreThreshold)) {
- ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
+ ScreenDecorations screenDecorations = mScreenDecorations.get();
if (screenDecorations == null) {
Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
} else {
@@ -256,7 +244,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
return;
}
- ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
+ ScreenDecorations screenDecorations = mScreenDecorations.get();
if (screenDecorations == null) {
Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable");
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
index 763315d2c4e2..a0d8b4c58f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
@@ -20,56 +20,82 @@ import android.content.Context;
import androidx.annotation.Nullable;
-import com.android.systemui.Dependency;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
/**
* Assistant Handle behavior that makes Assistant handles show/hide when the home handle is
* shown/hidden, respectively.
*/
+@Singleton
final class AssistHandleLikeHomeBehavior implements BehaviorController {
- private final StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
+ private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onStartedWakingUp() {
+ handleDozingChanged(/* isDozing = */ true);
+ }
+
+ @Override
+ public void onFinishedWakingUp() {
+ handleDozingChanged(/* isDozing = */ false);
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ handleDozingChanged(/* isDozing = */ true);
+ }
+
@Override
- public void onDozingChanged(boolean isDozing) {
- handleDozingChanged(isDozing);
+ public void onFinishedGoingToSleep() {
+ handleDozingChanged(/* isDozing = */ true);
}
};
private final SysUiState.SysUiStateCallback mSysUiStateCallback =
this::handleSystemUiStateChange;
- private final StatusBarStateController mStatusBarStateController;
- private final SysUiState mSysUiFlagContainer;
+
+ private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+ private final Lazy<SysUiState> mSysUiFlagContainer;
private boolean mIsDozing;
private boolean mIsHomeHandleHiding;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
- AssistHandleLikeHomeBehavior() {
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- mSysUiFlagContainer = Dependency.get(SysUiState.class);
+ @Inject
+ AssistHandleLikeHomeBehavior(
+ Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
+ Lazy<SysUiState> sysUiFlagContainer) {
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mSysUiFlagContainer = sysUiFlagContainer;
}
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
- mIsDozing = mStatusBarStateController.isDozing();
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mSysUiFlagContainer.addCallback(mSysUiStateCallback);
+ mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
+ != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
+ mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
callbackForCurrentState();
}
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
- mSysUiFlagContainer.removeCallback(mSysUiStateCallback);
+ mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
+ mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
}
private static boolean isHomeHandleHiding(int sysuiStateFlags) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java
index f4130aeb1d08..df913f9ce9cd 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java
@@ -20,9 +20,17 @@ import android.content.Context;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/** Assistant handle behavior that hides the Assistant handles. */
+@Singleton
final class AssistHandleOffBehavior implements BehaviorController {
+ @Inject
+ AssistHandleOffBehavior() {
+ }
+
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
callbacks.hide();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 5dd3cb1d8bdc..d371dbead42b 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -16,7 +16,8 @@
package com.android.systemui.assist;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.assist.AssistModule.ASSIST_HANDLE_THREAD_NAME;
+import static com.android.systemui.assist.AssistModule.UPTIME_NAME;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
@@ -26,14 +27,14 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
import android.os.Handler;
-import android.os.SystemClock;
import android.provider.Settings;
import androidx.annotation.Nullable;
+import androidx.slice.Clock;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.systemui.Dependency;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -49,11 +50,18 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
/**
* Assistant handle behavior that hides the handles when the phone is dozing or in immersive mode,
* shows the handles when on lockscreen, and shows the handles temporarily when changing tasks or
* entering overview.
*/
+@Singleton
final class AssistHandleReminderExpBehavior implements BehaviorController {
private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed";
@@ -86,11 +94,6 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
public void onStateChanged(int newState) {
handleStatusBarStateChanged(newState);
}
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- handleDozingChanged(isDozing);
- }
};
private final TaskStackChangeListener mTaskStackChangeListener =
new TaskStackChangeListener() {
@@ -111,9 +114,20 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
handleOverviewShown();
}
};
-
private final SysUiState.SysUiStateCallback mSysUiStateCallback =
this::handleSystemUiStateChanged;
+ private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedWakingUp() {
+ handleDozingChanged(false);
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ handleDozingChanged(true);
+ }
+ };
private final BroadcastReceiver mDefaultHomeBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -123,12 +137,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final IntentFilter mDefaultHomeIntentFilter;
private final Runnable mResetConsecutiveTaskSwitches = this::resetConsecutiveTaskSwitches;
+ private final Clock mClock;
private final Handler mHandler;
private final PhenotypeHelper mPhenotypeHelper;
- private final StatusBarStateController mStatusBarStateController;
- private final ActivityManagerWrapper mActivityManagerWrapper;
- private final OverviewProxyService mOverviewProxyService;
- private final SysUiState mSysUiFlagContainer;
+ private final Lazy<StatusBarStateController> mStatusBarStateController;
+ private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
+ private final Lazy<OverviewProxyService> mOverviewProxyService;
+ private final Lazy<SysUiState> mSysUiFlagContainer;
+ private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+ private final Lazy<PackageManagerWrapper> mPackageManagerWrapper;
private boolean mOnLockscreen;
private boolean mIsDozing;
@@ -150,13 +167,26 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Nullable private ComponentName mDefaultHome;
- AssistHandleReminderExpBehavior(Handler handler, PhenotypeHelper phenotypeHelper) {
+ @Inject
+ AssistHandleReminderExpBehavior(
+ @Named(UPTIME_NAME) Clock clock,
+ @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
+ PhenotypeHelper phenotypeHelper,
+ Lazy<StatusBarStateController> statusBarStateController,
+ Lazy<ActivityManagerWrapper> activityManagerWrapper,
+ Lazy<OverviewProxyService> overviewProxyService,
+ Lazy<SysUiState> sysUiFlagContainer,
+ Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
+ Lazy<PackageManagerWrapper> packageManagerWrapper) {
+ mClock = clock;
mHandler = handler;
mPhenotypeHelper = phenotypeHelper;
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- mSysUiFlagContainer = Dependency.get(SysUiState.class);
+ mStatusBarStateController = statusBarStateController;
+ mActivityManagerWrapper = activityManagerWrapper;
+ mOverviewProxyService = overviewProxyService;
+ mSysUiFlagContainer = sysUiFlagContainer;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mPackageManagerWrapper = packageManagerWrapper;
mDefaultHomeIntentFilter = new IntentFilter();
for (String action : DEFAULT_HOME_CHANGE_ACTIONS) {
mDefaultHomeIntentFilter.addAction(action);
@@ -170,14 +200,17 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mConsecutiveTaskSwitches = 0;
mDefaultHome = getCurrentDefaultHome();
context.registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
- mOnLockscreen = onLockscreen(mStatusBarStateController.getState());
- mIsDozing = mStatusBarStateController.isDozing();
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- ActivityManager.RunningTaskInfo runningTaskInfo = mActivityManagerWrapper.getRunningTask();
+ mOnLockscreen = onLockscreen(mStatusBarStateController.get().getState());
+ mStatusBarStateController.get().addCallback(mStatusBarStateListener);
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ mActivityManagerWrapper.get().getRunningTask();
mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId;
- mActivityManagerWrapper.registerTaskStackListener(mTaskStackChangeListener);
- mOverviewProxyService.addCallback(mOverviewProxyListener);
- mSysUiFlagContainer.addCallback(mSysUiStateCallback);
+ mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
+ mOverviewProxyService.get().addCallback(mOverviewProxyListener);
+ mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
+ mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
+ != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mLearningTimeElapsed = Settings.Secure.getLong(
context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0);
@@ -185,7 +218,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0);
mLearnedHintLastShownEpochDay = Settings.Secure.getLong(
context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0);
- mLastLearningTimestamp = SystemClock.uptimeMillis();
+ mLastLearningTimestamp = mClock.currentTimeMillis();
callbackForCurrentState(/* justUnlocked = */ false);
}
@@ -200,10 +233,11 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
mContext = null;
}
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackChangeListener);
- mOverviewProxyService.removeCallback(mOverviewProxyListener);
- mSysUiFlagContainer.removeCallback(mSysUiStateCallback);
+ mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
+ mActivityManagerWrapper.get().unregisterTaskStackListener(mTaskStackChangeListener);
+ mOverviewProxyService.get().removeCallback(mOverviewProxyListener);
+ mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
+ mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
}
@Override
@@ -223,15 +257,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
}
}
- private static boolean isNavBarHidden(int sysuiStateFlags) {
- return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
- }
-
@Nullable
- private static ComponentName getCurrentDefaultHome() {
+ private ComponentName getCurrentDefaultHome() {
List<ResolveInfo> homeActivities = new ArrayList<>();
- ComponentName defaultHome =
- PackageManagerWrapper.getInstance().getHomeActivities(homeActivities);
+ ComponentName defaultHome = mPackageManagerWrapper.get().getHomeActivities(homeActivities);
if (defaultHome != null) {
return defaultHome;
}
@@ -287,7 +316,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
}
private void handleSystemUiStateChanged(int sysuiStateFlags) {
- boolean isNavBarHidden = isNavBarHidden(sysuiStateFlags);
+ boolean isNavBarHidden =
+ (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
if (mIsNavBarHidden == isNavBarHidden) {
return;
}
@@ -374,24 +404,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- long currentTimestamp = SystemClock.uptimeMillis();
+ long currentTimestamp = mClock.currentTimeMillis();
mLearningTimeElapsed += currentTimestamp - mLastLearningTimestamp;
mLastLearningTimestamp = currentTimestamp;
- // TODO(b/140034473)
- whitelistIpcs(() -> Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed));
mIsLearned =
mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs();
- mHandler.post(this::recordLearnTimeElapsed);
- }
-
- private void recordLearnTimeElapsed() {
- if (mContext != null) {
- Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed);
- }
+ mHandler.post(() -> Settings.Secure.putLong(
+ mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed));
}
private void resetConsecutiveTaskSwitches() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index eb60b3db7a11..1f950f60fd6a 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -21,7 +21,6 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -157,17 +156,18 @@ public class AssistManager implements ConfigurationChangedReceiver {
};
@Inject
- public AssistManager(DeviceProvisionedController controller, Context context) {
+ public AssistManager(
+ DeviceProvisionedController controller,
+ Context context,
+ AssistUtils assistUtils,
+ AssistHandleBehaviorController handleController) {
mContext = context;
mDeviceProvisionedController = controller;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mAssistUtils = new AssistUtils(context);
+ mAssistUtils = assistUtils;
mAssistDisclosure = new AssistDisclosure(context, new Handler());
mPhoneStateMonitor = new PhoneStateMonitor(context);
- final HandlerThread assistHandleThread = new HandlerThread("AssistHandleThread");
- assistHandleThread.start();
- mHandleController = new AssistHandleBehaviorController(
- context, mAssistUtils, assistHandleThread.getThreadHandler());
+ mHandleController = handleController;
registerVoiceInteractionSessionListener();
mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
new file mode 100644
index 000000000000..2a82d215e44a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -0,0 +1,88 @@
+/*
+ * 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.assist;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+
+import androidx.slice.Clock;
+
+import com.android.internal.app.AssistUtils;
+import com.android.systemui.ScreenDecorations;
+import com.android.systemui.SysUiServiceProvider;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Module for dagger injections related to the Assistant. */
+@Module
+public abstract class AssistModule {
+
+ static final String ASSIST_HANDLE_THREAD_NAME = "assist_handle_thread";
+ static final String UPTIME_NAME = "uptime";
+
+ @Provides
+ @Singleton
+ @Named(ASSIST_HANDLE_THREAD_NAME)
+ static Handler provideBackgroundHandler() {
+ final HandlerThread backgroundHandlerThread =
+ new HandlerThread("AssistHandleThread");
+ backgroundHandlerThread.start();
+ return backgroundHandlerThread.getThreadHandler();
+ }
+
+ @Provides
+ @Singleton
+ static Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController>
+ provideAssistHandleBehaviorControllerMap(
+ AssistHandleOffBehavior offBehavior,
+ AssistHandleLikeHomeBehavior likeHomeBehavior,
+ AssistHandleReminderExpBehavior reminderExpBehavior) {
+ Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController> map =
+ new EnumMap<>(AssistHandleBehavior.class);
+ map.put(AssistHandleBehavior.OFF, offBehavior);
+ map.put(AssistHandleBehavior.LIKE_HOME, likeHomeBehavior);
+ map.put(AssistHandleBehavior.REMINDER_EXP, reminderExpBehavior);
+ return map;
+ }
+
+ @Provides
+ static ScreenDecorations provideScreenDecorations(Context context) {
+ return SysUiServiceProvider.getComponent(context, ScreenDecorations.class);
+ }
+
+ @Provides
+ @Singleton
+ static AssistUtils provideAssistUtils(Context context) {
+ return new AssistUtils(context);
+ }
+
+ @Provides
+ @Singleton
+ @Named(UPTIME_NAME)
+ static Clock provideSystemClock() {
+ return SystemClock::uptimeMillis;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
index c21a717dc8cf..cbb56e8e979d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
@@ -24,8 +24,18 @@ import androidx.annotation.Nullable;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Wrapper class for retrieving phenotype flag values.
+ *
+ * Can be mocked in tests for ease of testing the effects of particular values.
+ */
+@Singleton
public class PhenotypeHelper {
+ @Inject
public PhenotypeHelper() {}
public long getLong(String name, long defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index a68b61e4da45..1d47fc520ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -111,8 +111,12 @@ public class AuthBiometricFaceView extends AuthBiometricView {
R.string.biometric_dialog_face_icon_description_confirmed));
} else if (lastStateIsErrorIcon && newState == STATE_IDLE) {
animateOnce(R.drawable.face_dialog_error_to_idle);
+ mIconView.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) {
animateOnce(R.drawable.face_dialog_dark_to_checkmark);
+ mIconView.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_ERROR && lastState != STATE_ERROR) {
animateOnce(R.drawable.face_dialog_dark_to_error);
} else if (lastState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
@@ -125,6 +129,8 @@ public class AuthBiometricFaceView extends AuthBiometricView {
R.string.biometric_dialog_face_icon_description_authenticated));
} else if (newState == STATE_IDLE) {
showStaticDrawable(R.drawable.face_dialog_idle_static);
+ mIconView.setContentDescription(mContext.getString(
+ R.string.biometric_dialog_face_icon_description_idle));
} else {
Log.w(TAG, "Unhandled state: " + newState);
}
@@ -163,6 +169,11 @@ public class AuthBiometricFaceView extends AuthBiometricView {
}
@Override
+ protected boolean supportsSmallDialog() {
+ return true;
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mIconController = new IconController(mContext, mIconView, mIndicatorView);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
new file mode 100644
index 000000000000..176e9e6b1c9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
@@ -0,0 +1,147 @@
+/*
+ * 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.biometrics;
+
+
+import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+public class AuthBiometricFingerprintView extends AuthBiometricView {
+
+ private static final String TAG = "BiometricPrompt/AuthBiometricFingerprintView";
+
+ public AuthBiometricFingerprintView(Context context) {
+ this(context, null);
+ }
+
+ public AuthBiometricFingerprintView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected int getDelayAfterAuthenticatedDurationMs() {
+ return 0;
+ }
+
+ @Override
+ protected int getStateForAfterError() {
+ return STATE_AUTHENTICATING;
+ }
+
+ @Override
+ protected void handleResetAfterError() {
+ showTouchSensorString();
+ }
+
+ @Override
+ protected void handleResetAfterHelp() {
+ showTouchSensorString();
+ }
+
+ @Override
+ protected boolean supportsSmallDialog() {
+ return false;
+ }
+
+ @Override
+ public void updateState(@BiometricState int newState) {
+ updateIcon(mState, newState);
+
+ // Do this last since the state variable gets updated.
+ super.updateState(newState);
+ }
+
+ @Override
+ void onAttachedToWindowInternal() {
+ super.onAttachedToWindowInternal();
+ showTouchSensorString();
+ }
+
+ private void showTouchSensorString() {
+ mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
+ mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
+ }
+
+ private void updateIcon(int lastState, int newState) {
+ final Drawable icon = getAnimationForTransition(lastState, newState);
+ if (icon == null) {
+ Log.e(TAG, "Animation not found, " + lastState + " -> " + newState);
+ return;
+ }
+
+ final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+ ? (AnimatedVectorDrawable) icon
+ : null;
+
+ mIconView.setImageDrawable(icon);
+
+ if (animation != null && shouldAnimateForTransition(lastState, newState)) {
+ animation.forceAnimationOnUI();
+ animation.start();
+ }
+ }
+
+ private boolean shouldAnimateForTransition(int oldState, int newState) {
+ switch (newState) {
+ case STATE_HELP:
+ case STATE_ERROR:
+ return true;
+ case STATE_AUTHENTICATING_ANIMATING_IN:
+ case STATE_AUTHENTICATING:
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ return true;
+ } else {
+ return false;
+ }
+ case STATE_AUTHENTICATED:
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ private Drawable getAnimationForTransition(int oldState, int newState) {
+ int iconRes;
+
+ switch (newState) {
+ case STATE_HELP:
+ case STATE_ERROR:
+ iconRes = R.drawable.fingerprint_dialog_fp_to_error;
+ break;
+ case STATE_AUTHENTICATING_ANIMATING_IN:
+ case STATE_AUTHENTICATING:
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ iconRes = R.drawable.fingerprint_dialog_error_to_fp;
+ } else {
+ iconRes = R.drawable.fingerprint_dialog_fp_to_error;
+ }
+ break;
+ case STATE_AUTHENTICATED:
+ iconRes = R.drawable.fingerprint_dialog_fp_to_error;
+ break;
+ default:
+ return null;
+ }
+
+ return mContext.getDrawable(iconRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index df14a1716571..e4d2005c8867 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -32,6 +34,8 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -92,6 +96,7 @@ public abstract class AuthBiometricView extends LinearLayout {
int ACTION_USER_CANCELED = 2;
int ACTION_BUTTON_NEGATIVE = 3;
int ACTION_BUTTON_TRY_AGAIN = 4;
+ int ACTION_ERROR = 5;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -136,10 +141,15 @@ public abstract class AuthBiometricView extends LinearLayout {
public ImageView getIconView() {
return mBiometricView.findViewById(R.id.biometric_icon);
}
+
+ public int getDelayAfterError() {
+ return BiometricPrompt.HIDE_DIALOG_DELAY;
+ }
}
private final Injector mInjector;
private final Handler mHandler;
+ private final AccessibilityManager mAccessibilityManager;
private final int mTextColorError;
private final int mTextColorHint;
@@ -186,15 +196,14 @@ public abstract class AuthBiometricView extends LinearLayout {
*/
protected abstract void handleResetAfterHelp();
- private final Runnable mResetErrorRunnable = () -> {
- updateState(getStateForAfterError());
- handleResetAfterError();
- };
+ /**
+ * @return true if the dialog supports {@link AuthDialog.DialogSize#SIZE_SMALL}
+ */
+ protected abstract boolean supportsSmallDialog();
- private final Runnable mResetHelpRunnable = () -> {
- updateState(STATE_AUTHENTICATING);
- handleResetAfterHelp();
- };
+ private final Runnable mResetErrorRunnable;
+
+ private final Runnable mResetHelpRunnable;
private final OnClickListener mBackgroundClickListener = (view) -> {
if (mState == STATE_AUTHENTICATED) {
@@ -226,6 +235,20 @@ public abstract class AuthBiometricView extends LinearLayout {
mInjector = injector;
mInjector.mBiometricView = this;
+
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+ mResetErrorRunnable = () -> {
+ updateState(getStateForAfterError());
+ handleResetAfterError();
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
+ };
+
+ mResetHelpRunnable = () -> {
+ updateState(STATE_AUTHENTICATING);
+ handleResetAfterHelp();
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
+ };
}
public void setPanelController(AuthPanelController panelController) {
@@ -250,7 +273,7 @@ public abstract class AuthBiometricView extends LinearLayout {
@VisibleForTesting
void updateSize(@AuthDialog.DialogSize int newSize) {
- Log.v(TAG, "Current: " + mSize + " New: " + newSize);
+ Log.v(TAG, "Current size: " + mSize + " New size: " + newSize);
if (newSize == AuthDialog.SIZE_SMALL) {
mTitleView.setVisibility(View.GONE);
mSubtitleView.setVisibility(View.GONE);
@@ -323,6 +346,8 @@ public abstract class AuthBiometricView extends LinearLayout {
super.onAnimationEnd(animation);
mSize = newSize;
mDialogSizeAnimating = false;
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager,
+ AuthBiometricView.this);
}
});
@@ -338,6 +363,7 @@ public abstract class AuthBiometricView extends LinearLayout {
} else {
Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize);
}
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
public void updateState(@BiometricState int newState) {
@@ -359,6 +385,8 @@ public abstract class AuthBiometricView extends LinearLayout {
mNegativeButton.setVisibility(View.GONE);
mIndicatorView.setVisibility(View.INVISIBLE);
}
+ announceForAccessibility(getResources()
+ .getString(R.string.biometric_dialog_authenticated));
mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
getDelayAfterAuthenticatedDurationMs());
break;
@@ -385,6 +413,7 @@ public abstract class AuthBiometricView extends LinearLayout {
break;
}
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
mState = newState;
}
@@ -406,8 +435,18 @@ public abstract class AuthBiometricView extends LinearLayout {
updateState(STATE_ERROR);
}
+ public void onError(String error) {
+ showTemporaryMessage(error, mResetErrorRunnable);
+ updateState(STATE_ERROR);
+
+ mHandler.postDelayed(() -> {
+ mCallback.onAction(Callback.ACTION_ERROR);
+ }, mInjector.getDelayAfterError());
+ }
+
public void onHelp(String help) {
if (mSize != AuthDialog.SIZE_MEDIUM) {
+ Log.w(TAG, "Help received in size: " + mSize);
return;
}
showTemporaryMessage(help, mResetHelpRunnable);
@@ -441,6 +480,8 @@ public abstract class AuthBiometricView extends LinearLayout {
} else {
view.setText(string);
}
+
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
private void setText(TextView view, String string) {
@@ -459,6 +500,8 @@ public abstract class AuthBiometricView extends LinearLayout {
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY);
+
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
@Override
@@ -497,6 +540,7 @@ public abstract class AuthBiometricView extends LinearLayout {
updateState(STATE_AUTHENTICATING);
mCallback.onAction(Callback.ACTION_BUTTON_TRY_AGAIN);
mTryAgainButton.setVisibility(View.GONE);
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
});
}
@@ -527,15 +571,6 @@ public abstract class AuthBiometricView extends LinearLayout {
// Restore positive button state
mTryAgainButton.setVisibility(
mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
-
- // Restore indicator text state
- final String indicatorText =
- mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
- if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
- onHelp(indicatorText);
- } else if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
- onAuthenticationFailed(indicatorText);
- }
}
}
@@ -600,9 +635,20 @@ public abstract class AuthBiometricView extends LinearLayout {
if (mIconOriginalY == 0) {
mIconOriginalY = mIconView.getY();
if (mSavedState == null) {
- updateSize(mRequireConfirmation ? AuthDialog.SIZE_MEDIUM : AuthDialog.SIZE_SMALL);
+ updateSize(!mRequireConfirmation && supportsSmallDialog() ? AuthDialog.SIZE_SMALL
+ : AuthDialog.SIZE_MEDIUM);
} else {
updateSize(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE));
+
+ // Restore indicator text state only after size has been restored
+ final String indicatorText =
+ mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
+ if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
+ onHelp(indicatorText);
+ } else if (mSavedState.getBoolean(
+ AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
+ onAuthenticationFailed(indicatorText);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e198060da9aa..6555c75f677a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -19,12 +19,16 @@ package com.android.systemui.biometrics;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
+import android.os.UserManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -55,12 +59,12 @@ public class AuthContainerView extends LinearLayout
private static final int ANIMATION_DURATION_SHOW_MS = 250;
private static final int ANIMATION_DURATION_AWAY_MS = 350; // ms
- private static final int STATE_UNKNOWN = 0;
- private static final int STATE_ANIMATING_IN = 1;
- private static final int STATE_PENDING_DISMISS = 2;
- private static final int STATE_SHOWING = 3;
- private static final int STATE_ANIMATING_OUT = 4;
- private static final int STATE_GONE = 5;
+ static final int STATE_UNKNOWN = 0;
+ static final int STATE_ANIMATING_IN = 1;
+ static final int STATE_PENDING_DISMISS = 2;
+ static final int STATE_SHOWING = 3;
+ static final int STATE_ANIMATING_OUT = 4;
+ static final int STATE_GONE = 5;
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_UNKNOWN, STATE_ANIMATING_IN, STATE_PENDING_DISMISS, STATE_SHOWING,
@@ -87,6 +91,9 @@ public class AuthContainerView extends LinearLayout
private @ContainerState int mContainerState = STATE_UNKNOWN;
+ // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
+ @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
+
static class Config {
Context mContext;
AuthDialogCallback mCallback;
@@ -136,7 +143,8 @@ public class AuthContainerView extends LinearLayout
return this;
}
- public AuthContainerView build(int modalityMask) { // TODO
+ public AuthContainerView build(int modalityMask) {
+ mConfig.mModalityMask = modalityMask;
return new AuthContainerView(mConfig);
}
}
@@ -158,6 +166,9 @@ public class AuthContainerView extends LinearLayout
case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN:
mConfig.mCallback.onTryAgainPressed();
break;
+ case AuthBiometricView.Callback.ACTION_ERROR:
+ animateAway(AuthDialogCallback.DISMISSED_ERROR);
+ break;
default:
Log.e(TAG, "Unhandled action: " + action);
}
@@ -181,14 +192,36 @@ public class AuthContainerView extends LinearLayout
mContainerView = (ViewGroup) factory.inflate(
R.layout.auth_container_view, this, false /* attachToRoot */);
- // TODO: Depends on modality
- mBiometricView = (AuthBiometricFaceView)
- factory.inflate(R.layout.auth_biometric_face_view, null, false);
+ mPanelView = mContainerView.findViewById(R.id.panel);
+ mPanelController = new AuthPanelController(mContext, mPanelView);
+
+ // TODO: Update with new controllers if multi-modal authentication can occur simultaneously
+ if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ mBiometricView = (AuthBiometricFingerprintView)
+ factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
+ } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
+ mBiometricView = (AuthBiometricFaceView)
+ factory.inflate(R.layout.auth_biometric_face_view, null, false);
+ } else {
+ Log.e(TAG, "Unsupported modality mask: " + config.mModalityMask);
+ mBiometricView = null;
+ mBackgroundView = null;
+ mScrollView = null;
+ return;
+ }
mBackgroundView = mContainerView.findViewById(R.id.background);
- mPanelView = mContainerView.findViewById(R.id.panel);
- mPanelController = new AuthPanelController(mContext, mPanelView);
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ if (userManager.isManagedProfile(mConfig.mUserId)) {
+ final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background,
+ mContext.getTheme());
+ image.setColorFilter(dpm.getOrganizationColorForUser(mConfig.mUserId),
+ PorterDuff.Mode.DARKEN);
+ mBackgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ mBackgroundView.setImageDrawable(image);
+ }
mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
mBiometricView.setPanelController(mPanelController);
@@ -281,13 +314,13 @@ public class AuthContainerView extends LinearLayout
if (animate) {
animateAway(false /* sendReason */, 0 /* reason */);
} else {
- mWindowManager.removeView(this);
+ removeWindowIfAttached();
}
}
@Override
public void dismissFromSystemServer() {
- mWindowManager.removeView(this);
+ removeWindowIfAttached();
}
@Override
@@ -307,11 +340,12 @@ public class AuthContainerView extends LinearLayout
@Override
public void onError(String error) {
-
+ mBiometricView.onError(error);
}
@Override
public void onSaveState(@NonNull Bundle outState) {
+ outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState);
mBiometricView.onSaveState(outState);
}
@@ -338,12 +372,15 @@ public class AuthContainerView extends LinearLayout
}
mContainerState = STATE_ANIMATING_OUT;
+ if (sendReason) {
+ mPendingCallbackReason = reason;
+ } else {
+ mPendingCallbackReason = null;
+ }
+
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
- mWindowManager.removeView(this);
- if (sendReason) {
- mConfig.mCallback.onDismissed(reason);
- }
+ removeWindowIfAttached();
};
postOnAnimation(() -> {
@@ -369,6 +406,24 @@ public class AuthContainerView extends LinearLayout
});
}
+ private void sendPendingCallbackIfNotNull() {
+ Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
+ if (mPendingCallbackReason != null) {
+ mConfig.mCallback.onDismissed(mPendingCallbackReason);
+ mPendingCallbackReason = null;
+ }
+ }
+
+ private void removeWindowIfAttached() {
+ sendPendingCallbackIfNotNull();
+
+ if (mContainerState == STATE_GONE) {
+ return;
+ }
+ mContainerState = STATE_GONE;
+ mWindowManager.removeView(this);
+ }
+
private void onDialogAnimatedIn() {
if (mContainerState == STATE_PENDING_DISMISS) {
Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ab89034c3b65..d10a3fede412 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.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;
@@ -46,8 +45,6 @@ import java.util.List;
*/
public class AuthController extends SystemUI implements CommandQueue.Callbacks,
AuthDialogCallback {
- private static final String USE_NEW_DIALOG =
- "com.android.systemui.biometrics.AuthController.USE_NEW_DIALOG";
private static final String TAG = "BiometricPrompt/AuthController";
private static final boolean DEBUG = true;
@@ -305,31 +302,24 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mCurrentDialog.dismissWithoutCallback(false /* animate */);
mCurrentDialog = null;
- showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
+ // Only show the dialog if necessary. If it was animating out, the dialog is supposed
+ // to send its pending callback immediately.
+ if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
+ != AuthContainerView.STATE_ANIMATING_OUT) {
+ showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
+ }
}
}
protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
int userId, int type, String opPackageName, boolean skipIntro) {
- if (Settings.Secure.getIntForUser(
- mContext.getContentResolver(), USE_NEW_DIALOG, userId, 0) != 0) {
- return new AuthContainerView.Builder(mContext)
- .setCallback(this)
- .setBiometricPromptBundle(biometricPromptBundle)
- .setRequireConfirmation(requireConfirmation)
- .setUserId(userId)
- .setOpPackageName(opPackageName)
- .setSkipIntro(skipIntro)
- .build(type);
- } else {
- return new BiometricDialogView.Builder(mContext)
- .setCallback(this)
- .setBiometricPromptBundle(biometricPromptBundle)
- .setRequireConfirmation(requireConfirmation)
- .setUserId(userId)
- .setOpPackageName(opPackageName)
- .setSkipIntro(skipIntro)
- .build(type);
- }
+ return new AuthContainerView.Builder(mContext)
+ .setCallback(this)
+ .setBiometricPromptBundle(biometricPromptBundle)
+ .setRequireConfirmation(requireConfirmation)
+ .setUserId(userId)
+ .setOpPackageName(opPackageName)
+ .setSkipIntro(skipIntro)
+ .build(type);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index fb904231c175..edb29538874c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -29,6 +29,9 @@ import java.lang.annotation.RetentionPolicy;
* Interface for the biometric dialog UI.
*/
public interface AuthDialog {
+
+ String KEY_CONTAINER_STATE = "container_state";
+
String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility";
String KEY_BIOMETRIC_STATE = "state";
String KEY_BIOMETRIC_INDICATOR_STRING = "indicator_string"; // error / help / hint
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
deleted file mode 100644
index b985e1c2a4d4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.graphics.Outline;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.util.leak.RotationUtils;
-
-/**
- * Abstract base class. Shows a dialog for BiometricPrompt.
- */
-public abstract class BiometricDialogView extends LinearLayout implements AuthDialog {
-
- private static final String TAG = "BiometricPrompt/DialogView";
-
- public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
- public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
- public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
- public static final String KEY_STATE = "key_state";
- public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
- public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
- public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
- public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
- public static final String KEY_DIALOG_SIZE = "key_dialog_size";
-
- private static final int ANIMATION_DURATION_SHOW = 250; // ms
- private static final int ANIMATION_DURATION_AWAY = 350; // ms
-
- protected static final int MSG_RESET_MESSAGE = 1;
-
- protected static final int STATE_IDLE = 0;
- protected static final int STATE_AUTHENTICATING = 1;
- protected static final int STATE_ERROR = 2;
- protected static final int STATE_PENDING_CONFIRMATION = 3;
- protected static final int STATE_AUTHENTICATED = 4;
-
- // Dialog layout/animation
- private static final int IMPLICIT_Y_PADDING = 16; // dp
- private static final int GROW_DURATION = 150; // ms
- private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
- @VisibleForTesting static final int SIZE_UNKNOWN = 0;
- @VisibleForTesting static final int SIZE_SMALL = 1;
- @VisibleForTesting static final int SIZE_GROWING = 2;
- @VisibleForTesting static final int SIZE_BIG = 3;
- @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_GROWING, SIZE_BIG})
- @interface DialogSize {}
-
- @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle;
- private final AccessibilityManager mAccessibilityManager;
- private final IBinder mWindowToken = new Binder();
- private final Interpolator mLinearOutSlowIn;
- private final WindowManager mWindowManager;
- private final UserManager mUserManager;
- private final DevicePolicyManager mDevicePolicyManager;
- private final float mAnimationTranslationOffset;
- private final int mErrorColor;
- private final float mDialogWidth;
- protected final AuthDialogCallback mCallback;
- private final DialogOutlineProvider mOutlineProvider = new DialogOutlineProvider();
-
- protected final ViewGroup mLayout;
- protected final LinearLayout mDialog;
- @VisibleForTesting final TextView mTitleText;
- @VisibleForTesting final TextView mSubtitleText;
- @VisibleForTesting final TextView mDescriptionText;
- @VisibleForTesting final ImageView mBiometricIcon;
- @VisibleForTesting final TextView mErrorText;
- @VisibleForTesting final Button mPositiveButton;
- @VisibleForTesting final Button mNegativeButton;
- @VisibleForTesting final Button mTryAgainButton;
-
- protected final int mTextColor;
-
- private Bundle mBundle;
- private Bundle mRestoredState;
- private String mOpPackageName;
-
- private int mState = STATE_IDLE;
- private boolean mWasForceRemoved;
- private boolean mSkipIntro;
- protected boolean mRequireConfirmation;
- private int mUserId; // used to determine if we should show work background
- private @DialogSize int mSize;
- private float mIconOriginalY;
-
- private boolean mCompletedAnimatingIn;
- private boolean mPendingDismissDialog;
-
- protected abstract int getHintStringResourceId();
- protected abstract int getAuthenticatedAccessibilityResourceId();
- protected abstract int getIconDescriptionResourceId();
- protected abstract int getDelayAfterAuthenticatedDurationMs();
- protected abstract boolean shouldGrayAreaDismissDialog();
- protected abstract void handleResetMessage();
- protected abstract void updateIcon(int oldState, int newState);
- protected abstract boolean supportsSmallDialog();
-
- private final Runnable mShowAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(0)
- .setDuration(ANIMATION_DURATION_SHOW)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .withEndAction(() -> onDialogAnimatedIn())
- .start();
- }
- };
-
- @VisibleForTesting
- final WakefulnessLifecycle.Observer mWakefulnessObserver =
- new WakefulnessLifecycle.Observer() {
- @Override
- public void onStartedGoingToSleep() {
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
- }
- };
-
- private final class DialogOutlineProvider extends ViewOutlineProvider {
-
- float mY;
-
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(
- 0 /* left */,
- (int) mY, /* top */
- mDialog.getWidth() /* right */,
- mDialog.getBottom(), /* bottom */
- getResources().getDimension(R.dimen.biometric_dialog_corner_size));
- }
-
- int calculateSmall() {
- final float padding = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING);
- return mDialog.getHeight() - mBiometricIcon.getHeight() - 2 * (int) padding;
- }
-
- void setOutlineY(float y) {
- mY = y;
- }
- }
-
- protected Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_RESET_MESSAGE:
- handleResetMessage();
- break;
- default:
- Log.e(TAG, "Unhandled message: " + msg.what);
- break;
- }
- }
- };
-
- /**
- * Builds the dialog with specified parameters.
- */
- public static class Builder {
- public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT;
- public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE;
-
- private Context mContext;
- private AuthDialogCallback mCallback;
- private Bundle mBundle;
- private boolean mRequireConfirmation;
- private int mUserId;
- private String mOpPackageName;
- private boolean mSkipIntro;
-
- public Builder(Context context) {
- mContext = context;
- }
-
- public Builder setCallback(AuthDialogCallback callback) {
- mCallback = callback;
- return this;
- }
-
- public Builder setBiometricPromptBundle(Bundle bundle) {
- mBundle = bundle;
- return this;
- }
-
- public Builder setRequireConfirmation(boolean requireConfirmation) {
- mRequireConfirmation = requireConfirmation;
- return this;
- }
-
- public Builder setUserId(int userId) {
- mUserId = userId;
- return this;
- }
-
- public Builder setOpPackageName(String opPackageName) {
- mOpPackageName = opPackageName;
- return this;
- }
-
- public Builder setSkipIntro(boolean skipIntro) {
- mSkipIntro = skipIntro;
- return this;
- }
-
- public BiometricDialogView build(int type) {
- return build(type, new Injector());
- }
-
- public BiometricDialogView build(int type, Injector injector) {
- BiometricDialogView dialog;
- if (type == TYPE_FINGERPRINT) {
- dialog = new FingerprintDialogView(mContext, mCallback, injector);
- } else if (type == TYPE_FACE) {
- dialog = new FaceDialogView(mContext, mCallback, injector);
- } else {
- return null;
- }
- dialog.setBundle(mBundle);
- dialog.setRequireConfirmation(mRequireConfirmation);
- dialog.setUserId(mUserId);
- dialog.setOpPackageName(mOpPackageName);
- dialog.setSkipIntro(mSkipIntro);
- return dialog;
- }
- }
-
- public static class Injector {
- public WakefulnessLifecycle getWakefulnessLifecycle() {
- return Dependency.get(WakefulnessLifecycle.class);
- }
- }
-
- protected BiometricDialogView(Context context, AuthDialogCallback callback, Injector injector) {
- super(context);
- mWakefulnessLifecycle = injector.getWakefulnessLifecycle();
-
- mCallback = callback;
- mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
- mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mUserManager = mContext.getSystemService(UserManager.class);
- mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
- mAnimationTranslationOffset = getResources()
- .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
- mErrorColor = getResources().getColor(R.color.biometric_dialog_error);
- mTextColor = getResources().getColor(R.color.biometric_dialog_gray);
-
- DisplayMetrics metrics = new DisplayMetrics();
- mWindowManager.getDefaultDisplay().getMetrics(metrics);
- mDialogWidth = Math.min(metrics.widthPixels, metrics.heightPixels);
-
- // Create the dialog
- LayoutInflater factory = LayoutInflater.from(getContext());
- mLayout = (ViewGroup) factory.inflate(R.layout.biometric_dialog, this, false);
- addView(mLayout);
-
- mLayout.setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_BACK) {
- return false;
- }
- if (event.getAction() == KeyEvent.ACTION_UP) {
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
- }
- return true;
- }
- });
-
- final View space = mLayout.findViewById(R.id.space);
- final View leftSpace = mLayout.findViewById(R.id.left_space);
- final View rightSpace = mLayout.findViewById(R.id.right_space);
-
- mDialog = mLayout.findViewById(R.id.dialog);
- mTitleText = mLayout.findViewById(R.id.title);
- mSubtitleText = mLayout.findViewById(R.id.subtitle);
- mDescriptionText = mLayout.findViewById(R.id.description);
- mBiometricIcon = mLayout.findViewById(R.id.biometric_icon);
- mErrorText = mLayout.findViewById(R.id.error);
- mNegativeButton = mLayout.findViewById(R.id.button2);
- mPositiveButton = mLayout.findViewById(R.id.button1);
- mTryAgainButton = mLayout.findViewById(R.id.button_try_again);
-
- mBiometricIcon.setContentDescription(
- getResources().getString(getIconDescriptionResourceId()));
-
- setDismissesDialog(space);
- setDismissesDialog(leftSpace);
- setDismissesDialog(rightSpace);
-
- mNegativeButton.setOnClickListener((View v) -> {
- if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) {
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
- } else {
- animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
- }
- });
-
- mPositiveButton.setOnClickListener((View v) -> {
- updateState(STATE_AUTHENTICATED);
- mHandler.postDelayed(() -> {
- animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
- }, getDelayAfterAuthenticatedDurationMs());
- });
-
- mTryAgainButton.setOnClickListener((View v) -> {
- handleResetMessage();
- updateState(STATE_AUTHENTICATING);
- showTryAgainButton(false /* show */);
-
- mPositiveButton.setVisibility(View.VISIBLE);
- mPositiveButton.setEnabled(false);
-
- mCallback.onTryAgainPressed();
- });
-
- // Must set these in order for the back button events to be received.
- mLayout.setFocusableInTouchMode(true);
- mLayout.requestFocus();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
-
- final ImageView backgroundView = mLayout.findViewById(R.id.background);
- if (mUserManager.isManagedProfile(mUserId)) {
- final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background,
- mContext.getTheme());
- image.setColorFilter(mDevicePolicyManager.getOrganizationColorForUser(mUserId),
- PorterDuff.Mode.DARKEN);
- backgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP);
- backgroundView.setImageDrawable(image);
- } else {
- backgroundView.setImageDrawable(null);
- backgroundView.setBackgroundColor(R.color.biometric_dialog_dim_color);
- }
-
- mNegativeButton.setVisibility(View.VISIBLE);
- mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
-
- if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
- mDialog.getLayoutParams().width = (int) mDialogWidth;
- }
-
- if (mRestoredState == null) {
- updateState(STATE_AUTHENTICATING);
- final int hint = getHintStringResourceId();
- if (hint != 0) {
- mErrorText.setText(hint);
- mErrorText.setContentDescription(mContext.getString(hint));
- mErrorText.setVisibility(View.VISIBLE);
- } else {
- mErrorText.setVisibility(View.INVISIBLE);
- }
- announceAccessibilityEvent();
- } else {
- updateState(mState);
- }
-
- CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE);
-
- mTitleText.setVisibility(View.VISIBLE);
- mTitleText.setText(titleText);
-
- final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
- if (TextUtils.isEmpty(subtitleText)) {
- mSubtitleText.setVisibility(View.GONE);
- announceAccessibilityEvent();
- } else {
- mSubtitleText.setVisibility(View.VISIBLE);
- mSubtitleText.setText(subtitleText);
- }
-
- final CharSequence descriptionText =
- mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
- if (TextUtils.isEmpty(descriptionText)) {
- mDescriptionText.setVisibility(View.GONE);
- announceAccessibilityEvent();
- } else {
- mDescriptionText.setVisibility(View.VISIBLE);
- mDescriptionText.setText(descriptionText);
- }
-
- if (requiresConfirmation() && mRestoredState == null) {
- mPositiveButton.setVisibility(View.VISIBLE);
- mPositiveButton.setEnabled(false);
- }
-
- if (mWasForceRemoved || mSkipIntro) {
- // Show the dialog immediately
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mDialog.setAlpha(1.0f);
- mDialog.setTranslationY(0);
- mLayout.setAlpha(1.0f);
- mCompletedAnimatingIn = true;
- } else {
- // Dim the background and slide the dialog up
- mDialog.setTranslationY(mAnimationTranslationOffset);
- mLayout.setAlpha(0f);
- postOnAnimation(mShowAnimationRunnable);
- }
- mWasForceRemoved = false;
- mSkipIntro = false;
- }
-
- /**
- * Do small/big layout here instead of onAttachedToWindow, since:
- * 1) We need the big layout to be measured, etc for small -> big animation
- * 2) We need the dialog measurements to know where to move the biometric icon to
- *
- * BiometricDialogView already sets the views to their default big state, so here we only
- * need to hide the ones that are unnecessary.
- */
- @Override
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (mIconOriginalY == 0) {
- mIconOriginalY = mBiometricIcon.getY();
- }
-
- // UNKNOWN means size hasn't been set yet. First time we create the dialog.
- // onLayout can happen when visibility of views change (during animation, etc).
- if (getSize() != SIZE_UNKNOWN) {
- // Probably not the cleanest way to do this, but since dialog is big by default,
- // and small dialogs can persist across orientation changes, we need to set it to
- // small size here again.
- if (getSize() == SIZE_SMALL) {
- updateSize(SIZE_SMALL);
- }
- return;
- }
-
- // If we don't require confirmation, show the small dialog first (until errors occur).
- if (!requiresConfirmation() && supportsSmallDialog()) {
- updateSize(SIZE_SMALL);
- } else {
- updateSize(SIZE_BIG);
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
- }
-
- @VisibleForTesting
- void updateSize(@DialogSize int newSize) {
- final float padding = Utils.dpToPixels(mContext, IMPLICIT_Y_PADDING);
- final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
-
- if (newSize == SIZE_SMALL) {
- if (!supportsSmallDialog()) {
- Log.e(TAG, "Small dialog unsupported");
- return;
- }
-
- // These fields are required and/or always hold a spot on the UI, so should be set to
- // INVISIBLE so they keep their position
- mTitleText.setVisibility(View.INVISIBLE);
- mErrorText.setVisibility(View.INVISIBLE);
- mNegativeButton.setVisibility(View.INVISIBLE);
-
- // These fields are optional, so set them to gone or invisible depending on their
- // usage. If they're empty, they're already set to GONE in BiometricDialogView.
- if (!TextUtils.isEmpty(mSubtitleText.getText())) {
- mSubtitleText.setVisibility(View.INVISIBLE);
- }
- if (!TextUtils.isEmpty(mDescriptionText.getText())) {
- mDescriptionText.setVisibility(View.INVISIBLE);
- }
-
- // Move the biometric icon to the small spot
- mBiometricIcon.setY(iconSmallPositionY);
-
- // Clip the dialog to the small size
- mDialog.setOutlineProvider(mOutlineProvider);
- mOutlineProvider.setOutlineY(mOutlineProvider.calculateSmall());
-
- mDialog.setClipToOutline(true);
- mDialog.invalidateOutline();
-
- mSize = newSize;
- announceAccessibilityEvent();
- } else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) {
- mSize = SIZE_GROWING;
-
- // Animate the outline
- final ValueAnimator outlineAnimator =
- ValueAnimator.ofFloat(mOutlineProvider.calculateSmall(), 0);
- outlineAnimator.addUpdateListener((animation) -> {
- final float y = (float) animation.getAnimatedValue();
- mOutlineProvider.setOutlineY(y);
- mDialog.invalidateOutline();
- });
-
- // Animate the icon back to original big position
- final ValueAnimator iconAnimator =
- ValueAnimator.ofFloat(iconSmallPositionY, mIconOriginalY);
- iconAnimator.addUpdateListener((animation) -> {
- final float y = (float) animation.getAnimatedValue();
- mBiometricIcon.setY(y);
- });
-
- // Animate the error text so it slides up with the icon
- final ValueAnimator textSlideAnimator =
- ValueAnimator.ofFloat(Utils.dpToPixels(mContext, TEXT_ANIMATE_DISTANCE), 0);
- textSlideAnimator.addUpdateListener((animation) -> {
- final float y = (float) animation.getAnimatedValue();
- mErrorText.setTranslationY(y);
- });
-
- // Opacity animator for things that should fade in (title, subtitle, details, negative
- // button)
- final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
- opacityAnimator.addUpdateListener((animation) -> {
- final float opacity = (float) animation.getAnimatedValue();
-
- // These fields are required and/or always hold a spot on the UI
- mTitleText.setAlpha(opacity);
- mErrorText.setAlpha(opacity);
- mNegativeButton.setAlpha(opacity);
- mTryAgainButton.setAlpha(opacity);
-
- // These fields are optional, so only animate them if they're supposed to be showing
- if (!TextUtils.isEmpty(mSubtitleText.getText())) {
- mSubtitleText.setAlpha(opacity);
- }
- if (!TextUtils.isEmpty(mDescriptionText.getText())) {
- mDescriptionText.setAlpha(opacity);
- }
- });
-
- // Choreograph together
- final AnimatorSet as = new AnimatorSet();
- as.setDuration(GROW_DURATION);
- as.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- // Set the visibility of opacity-animating views back to VISIBLE
- mTitleText.setVisibility(View.VISIBLE);
- mErrorText.setVisibility(View.VISIBLE);
- mNegativeButton.setVisibility(View.VISIBLE);
- mTryAgainButton.setVisibility(View.VISIBLE);
-
- if (!TextUtils.isEmpty(mSubtitleText.getText())) {
- mSubtitleText.setVisibility(View.VISIBLE);
- }
- if (!TextUtils.isEmpty(mDescriptionText.getText())) {
- mDescriptionText.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mSize = SIZE_BIG;
- }
- });
- as.play(outlineAnimator).with(iconAnimator).with(opacityAnimator)
- .with(textSlideAnimator);
- as.start();
- } else if (mSize == SIZE_BIG) {
- mDialog.setClipToOutline(false);
- mDialog.invalidateOutline();
-
- mBiometricIcon.setY(mIconOriginalY);
-
- mSize = newSize;
- }
- }
-
- private void setDismissesDialog(View v) {
- v.setClickable(true);
- v.setOnClickListener(v1 -> {
- if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
- }
- });
- }
-
- private void animateAway(@AuthDialogCallback.DismissedReason int reason) {
- animateAway(true /* sendReason */, reason);
- }
-
- /**
- * Animate the dialog away
- * @param reason one of the {@link AuthDialogCallback} codes
- */
- private void animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason) {
- if (!mCompletedAnimatingIn) {
- Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn");
- mPendingDismissDialog = true;
- return;
- }
-
- // This is where final cleanup should occur.
- final Runnable endActionRunnable = new Runnable() {
- @Override
- public void run() {
- mWindowManager.removeView(BiometricDialogView.this);
- // Set the icons / text back to normal state
- handleResetMessage();
- showTryAgainButton(false /* show */);
- updateState(STATE_IDLE);
- if (sendReason) {
- mCallback.onDismissed(reason);
- }
- }
- };
-
- postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mLayout.animate()
- .alpha(0f)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .start();
- mDialog.animate()
- .translationY(mAnimationTranslationOffset)
- .setDuration(ANIMATION_DURATION_AWAY)
- .setInterpolator(mLinearOutSlowIn)
- .withLayer()
- .withEndAction(endActionRunnable)
- .start();
- }
- });
- }
-
- /**
- * Skip the intro animation
- */
- private void setSkipIntro(boolean skip) {
- mSkipIntro = skip;
- }
-
- private void setBundle(Bundle bundle) {
- mBundle = bundle;
- }
-
- private void setRequireConfirmation(boolean requireConfirmation) {
- mRequireConfirmation = requireConfirmation;
- }
-
- protected boolean requiresConfirmation() {
- return mRequireConfirmation;
- }
-
- private void setUserId(int userId) {
- mUserId = userId;
- }
-
- private void setOpPackageName(String opPackageName) {
- mOpPackageName = opPackageName;
- }
-
- // Shows an error/help message
- protected void showTemporaryMessage(String message) {
- mHandler.removeMessages(MSG_RESET_MESSAGE);
- mErrorText.setText(message);
- mErrorText.setTextColor(mErrorColor);
- mErrorText.setContentDescription(message);
- mErrorText.setVisibility(View.VISIBLE);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
-
- @Override
- public void show(WindowManager wm, @Nullable Bundle savedState) {
- if (savedState != null) {
- restoreState(savedState);
- }
- wm.addView(this, getLayoutParams(mWindowToken));
- }
-
- /**
- * Force remove the window, cancelling any animation that's happening. This should only be
- * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
- * will cause the dialog to show without an animation the next time it's attached.
- */
- @Override
- public void dismissWithoutCallback(boolean animate) {
- if (animate) {
- animateAway(false /* sendReason */, 0 /* reason */);
- } else {
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mWindowManager.removeView(BiometricDialogView.this);
- mWasForceRemoved = true;
- }
- }
-
- @Override
- public void dismissFromSystemServer() {
- animateAway(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
- }
-
- @Override
- public void onAuthenticationSucceeded() {
- announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId()));
-
- if (requiresConfirmation()) {
- updateState(STATE_PENDING_CONFIRMATION);
- } else {
- mHandler.postDelayed(() -> {
- animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED);
- }, getDelayAfterAuthenticatedDurationMs());
-
- updateState(STATE_AUTHENTICATED);
- }
- }
-
-
- @Override
- public void onAuthenticationFailed(String message) {
- updateState(STATE_ERROR);
- showTemporaryMessage(message);
- }
-
- /**
- * Transient help message (acquire) is received, dialog stays showing. Sensor stays in
- * "authenticating" state.
- * @param message
- */
- @Override
- public void onHelp(String message) {
- updateState(STATE_ERROR);
- showTemporaryMessage(message);
- }
-
- /**
- * Hard error is received, dialog will be dismissed soon.
- * @param error
- */
- @Override
- public void onError(String error) {
- // All error messages will cause the dialog to go from small -> big. Error messages
- // are messages such as lockout, auth failed, etc.
- if (mSize == SIZE_SMALL) {
- updateSize(SIZE_BIG);
- }
-
- updateState(STATE_ERROR);
- showTemporaryMessage(error);
- showTryAgainButton(false /* show */);
-
- mHandler.postDelayed(() -> {
- animateAway(AuthDialogCallback.DISMISSED_ERROR);
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
- }
-
-
- @Override
- public void onSaveState(Bundle bundle) {
- bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
- bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
- bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
- bundle.putInt(KEY_STATE, mState);
- bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
- bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
- bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
- bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
- bundle.putInt(KEY_DIALOG_SIZE, mSize);
- }
-
- public void restoreState(Bundle bundle) {
- mRestoredState = bundle;
-
- // Keep in mind that this happens before onAttachedToWindow()
- mSize = bundle.getInt(KEY_DIALOG_SIZE);
-
- final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
- mTryAgainButton.setVisibility(tryAgainVisibility);
- final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
- mPositiveButton.setVisibility(confirmVisibility);
- final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
- mPositiveButton.setEnabled(confirmEnabled);
- mState = bundle.getInt(KEY_STATE);
- mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
- mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
- final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
- mErrorText.setVisibility(errorTextVisibility);
- if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
- || confirmVisibility == View.INVISIBLE) {
- announceAccessibilityEvent();
- }
- mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
- if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
- }
-
- @Override
- public String getOpPackageName() {
- return mOpPackageName;
- }
-
- protected void updateState(int newState) {
- if (newState == STATE_PENDING_CONFIRMATION) {
- mHandler.removeMessages(MSG_RESET_MESSAGE);
- mErrorText.setTextColor(mTextColor);
- mErrorText.setText(R.string.biometric_dialog_tap_confirm);
- mErrorText.setContentDescription(
- getResources().getString(R.string.biometric_dialog_tap_confirm));
- mErrorText.setVisibility(View.VISIBLE);
- announceAccessibilityEvent();
- mPositiveButton.setVisibility(View.VISIBLE);
- mPositiveButton.setEnabled(true);
- } else if (newState == STATE_AUTHENTICATED) {
- mPositiveButton.setVisibility(View.GONE);
- mNegativeButton.setVisibility(View.GONE);
- mErrorText.setVisibility(View.INVISIBLE);
- announceAccessibilityEvent();
- }
-
- if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) {
- mNegativeButton.setText(R.string.cancel);
- mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
- }
-
- updateIcon(mState, newState);
- mState = newState;
- }
-
- protected @DialogSize int getSize() {
- return mSize;
- }
-
- protected void showTryAgainButton(boolean show) {
- if (show && getSize() == SIZE_SMALL) {
- // Do not call super, we will nicely animate the alpha together with the rest
- // of the elements in here.
- updateSize(SIZE_BIG);
- } else {
- if (show) {
- mTryAgainButton.setVisibility(View.VISIBLE);
- } else {
- mTryAgainButton.setVisibility(View.GONE);
- announceAccessibilityEvent();
- }
- }
-
- if (show) {
- mPositiveButton.setVisibility(View.GONE);
- announceAccessibilityEvent();
- }
- }
-
- protected void onDialogAnimatedIn() {
- mCompletedAnimatingIn = true;
-
- if (mPendingDismissDialog) {
- Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
- animateAway(false /* sendReason */, 0);
- mPendingDismissDialog = false;
- }
- }
-
- /**
- * @param windowToken token for the window
- * @return
- */
- public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("BiometricDialogView");
- lp.token = windowToken;
- return lp;
- }
-
- // Every time a view becomes invisible we need to announce an accessibility event.
- // This is due to an issue in the framework, b/132298701 recommended this workaround.
- protected void announceAccessibilityEvent() {
- if (!mAccessibilityManager.isEnabled()) {
- return;
- }
- AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE);
- mDialog.sendAccessibilityEventUnchecked(event);
- mDialog.notifySubtreeAccessibilityStateChanged(mDialog, mDialog,
- CONTENT_CHANGE_TYPE_SUBTREE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
deleted file mode 100644
index d5dcbf126b63..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.content.Context;
-import android.graphics.drawable.Animatable2;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
- * and positive/negative buttons.
- */
-public class FaceDialogView extends BiometricDialogView {
-
- private static final String TAG = "BiometricPrompt/FaceDialogView";
-
- private static final String KEY_DIALOG_ANIMATED_IN = "key_dialog_animated_in";
-
- private static final int HIDE_DIALOG_DELAY = 500; // ms
-
- private IconController mIconController;
- private boolean mDialogAnimatedIn;
-
- /**
- * Class that handles the biometric icon animations.
- */
- private final class IconController extends Animatable2.AnimationCallback {
-
- private boolean mLastPulseDirection; // false = dark to light, true = light to dark
-
- int mState;
-
- IconController() {
- mState = STATE_IDLE;
- }
-
- public void animateOnce(int iconRes) {
- animateIcon(iconRes, false);
- }
-
- public void showStatic(int iconRes) {
- mBiometricIcon.setImageDrawable(mContext.getDrawable(iconRes));
- }
-
- public void startPulsing() {
- mLastPulseDirection = false;
- animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true);
- }
-
- public void showIcon(int iconRes) {
- final Drawable drawable = mContext.getDrawable(iconRes);
- mBiometricIcon.setImageDrawable(drawable);
- }
-
- private void animateIcon(int iconRes, boolean repeat) {
- final AnimatedVectorDrawable icon =
- (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
- mBiometricIcon.setImageDrawable(icon);
- icon.forceAnimationOnUI();
- if (repeat) {
- icon.registerAnimationCallback(this);
- }
- icon.start();
- }
-
- private void pulseInNextDirection() {
- int iconRes = mLastPulseDirection ? R.drawable.face_dialog_pulse_dark_to_light
- : R.drawable.face_dialog_pulse_light_to_dark;
- animateIcon(iconRes, true /* repeat */);
- mLastPulseDirection = !mLastPulseDirection;
- }
-
- @Override
- public void onAnimationEnd(Drawable drawable) {
- super.onAnimationEnd(drawable);
-
- if (mState == STATE_AUTHENTICATING) {
- // Still authenticating, pulse the icon
- pulseInNextDirection();
- }
- }
- }
-
- private final Runnable mErrorToIdleAnimationRunnable = () -> {
- updateState(STATE_IDLE);
- mErrorText.setVisibility(View.INVISIBLE);
- announceAccessibilityEvent();
- };
-
- protected FaceDialogView(Context context, AuthDialogCallback callback, Injector injector) {
- super(context, callback, injector);
- mIconController = new IconController();
- }
-
- @Override
- public void onSaveState(Bundle bundle) {
- super.onSaveState(bundle);
- bundle.putBoolean(KEY_DIALOG_ANIMATED_IN, mDialogAnimatedIn);
- }
-
-
- @Override
- protected void handleResetMessage() {
- mErrorText.setTextColor(mTextColor);
- mErrorText.setVisibility(View.INVISIBLE);
- announceAccessibilityEvent();
- }
-
- @Override
- public void restoreState(Bundle bundle) {
- super.restoreState(bundle);
- mDialogAnimatedIn = bundle.getBoolean(KEY_DIALOG_ANIMATED_IN);
- }
-
- @Override
- public void onAuthenticationFailed(String message) {
- super.onAuthenticationFailed(message);
- showTryAgainButton(true);
- }
-
- @Override
- protected int getHintStringResourceId() {
- return 0;
- }
-
- @Override
- protected int getAuthenticatedAccessibilityResourceId() {
- if (mRequireConfirmation) {
- return com.android.internal.R.string.face_authenticated_confirmation_required;
- } else {
- return com.android.internal.R.string.face_authenticated_no_confirmation_required;
- }
- }
-
- @Override
- protected int getIconDescriptionResourceId() {
- return R.string.accessibility_face_dialog_face_icon;
- }
-
- @Override
- protected void updateIcon(int oldState, int newState) {
- mIconController.mState = newState;
-
- if (newState == STATE_AUTHENTICATING) {
- mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
- if (mDialogAnimatedIn) {
- mIconController.startPulsing();
- } else {
- mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light);
- }
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticating));
- } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
- mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_confirmed));
- } else if (oldState == STATE_ERROR && newState == STATE_IDLE) {
- mIconController.animateOnce(R.drawable.face_dialog_error_to_idle);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_idle));
- } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) {
- mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
- mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_ERROR) {
- // It's easier to only check newState and gate showing the animation on the
- // mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example,
- // we may go from error -> error due to configuration change which is valid and we
- // should show the animation, or we can go from error -> error by receiving repeated
- // acquire messages in which case we do not want to repeatedly start the animation.
- if (!mHandler.hasCallbacks(mErrorToIdleAnimationRunnable)) {
- mIconController.animateOnce(R.drawable.face_dialog_dark_to_error);
- mHandler.postDelayed(mErrorToIdleAnimationRunnable,
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
- } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
- mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_PENDING_CONFIRMATION) {
- mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
- mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_authenticated));
- } else if (newState == STATE_IDLE) {
- mIconController.showStatic(R.drawable.face_dialog_idle_static);
- mBiometricIcon.setContentDescription(mContext.getString(
- R.string.biometric_dialog_face_icon_description_idle));
- } else {
- Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState);
- }
-
- // Note that this must be after the newState == STATE_ERROR check above since this affects
- // the logic.
- if (oldState == STATE_ERROR && newState == STATE_ERROR) {
- // Keep the error icon and text around for a while longer if we keep receiving
- // STATE_ERROR
- mHandler.removeCallbacks(mErrorToIdleAnimationRunnable);
- mHandler.postDelayed(mErrorToIdleAnimationRunnable, BiometricPrompt.HIDE_DIALOG_DELAY);
- }
- }
-
- @Override
- protected boolean supportsSmallDialog() {
- return true;
- }
-
- @Override
- public void onDialogAnimatedIn() {
- super.onDialogAnimatedIn();
- mDialogAnimatedIn = true;
- mIconController.startPulsing();
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return HIDE_DIALOG_DELAY;
- }
-
- @Override
- protected boolean shouldGrayAreaDismissDialog() {
- if (getSize() == SIZE_SMALL) {
- return false;
- }
- return true;
- }
-
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
deleted file mode 100644
index cda217619eed..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.content.Context;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import com.android.systemui.R;
-
-/**
- * This class loads the view for the system-provided dialog. The view consists of:
- * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area,
- * and positive/negative buttons.
- */
-public class FingerprintDialogView extends BiometricDialogView {
-
- private static final String TAG = "BiometricPrompt/FingerprintDialogView";
-
- protected FingerprintDialogView(Context context, AuthDialogCallback callback,
- Injector injector) {
- super(context, callback, injector);
- }
-
- @Override
- protected void handleResetMessage() {
- updateState(STATE_AUTHENTICATING);
- mErrorText.setText(getHintStringResourceId());
- mErrorText.setTextColor(mTextColor);
- }
-
- @Override
- protected int getHintStringResourceId() {
- return R.string.fingerprint_dialog_touch_sensor;
- }
-
- @Override
- protected int getAuthenticatedAccessibilityResourceId() {
- return com.android.internal.R.string.fingerprint_authenticated;
- }
-
- @Override
- protected int getIconDescriptionResourceId() {
- return R.string.accessibility_fingerprint_dialog_fingerprint_icon;
- }
-
- @Override
- protected void updateIcon(int lastState, int newState) {
- final Drawable icon = getAnimationForTransition(lastState, newState);
- if (icon == null) {
- Log.e(TAG, "Animation not found, " + lastState + " -> " + newState);
- return;
- }
-
- final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
- ? (AnimatedVectorDrawable) icon
- : null;
-
- mBiometricIcon.setImageDrawable(icon);
-
- if (animation != null && shouldAnimateForTransition(lastState, newState)) {
- animation.forceAnimationOnUI();
- animation.start();
- }
- }
-
- @Override
- protected boolean supportsSmallDialog() {
- return false;
- }
-
- protected boolean shouldAnimateForTransition(int oldState, int newState) {
- if (newState == STATE_ERROR) {
- return true;
- } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
- return true;
- } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
- // TODO(b/77328470): add animation when fingerprint is authenticated
- return false;
- } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) {
- // TODO(b/77328470): add animation when fingerprint is authenticated
- return false;
- } else if (newState == STATE_AUTHENTICATING) {
- return false;
- }
- return false;
- }
-
- @Override
- protected int getDelayAfterAuthenticatedDurationMs() {
- return 0;
- }
-
- @Override
- protected boolean shouldGrayAreaDismissDialog() {
- // Fingerprint dialog always dismisses when region outside the dialog is tapped
- return true;
- }
-
- protected Drawable getAnimationForTransition(int oldState, int newState) {
- int iconRes;
- if (newState == STATE_ERROR) {
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATING) {
- iconRes = R.drawable.fingerprint_dialog_error_to_fp;
- } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
- // TODO(b/77328470): add animation when fingerprint is authenticated
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) {
- // TODO(b/77328470): add animation when fingerprint is authenticated
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else if (newState == STATE_AUTHENTICATING) {
- iconRes = R.drawable.fingerprint_dialog_fp_to_error;
- } else {
- return null;
- }
- return mContext.getDrawable(iconRes);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index edd8089c35f9..e00cf6abafaa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,8 +16,14 @@
package com.android.systemui.biometrics;
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+
import android.content.Context;
import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
public class Utils {
static float dpToPixels(Context context, float dp) {
@@ -29,4 +35,15 @@ public class Utils {
return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT);
}
+
+ static void notifyAccessibilityContentChanged(AccessibilityManager am, ViewGroup view) {
+ if (!am.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE);
+ view.sendAccessibilityEventUnchecked(event);
+ view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 94d9ede5c8b1..67fc3e32883f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -511,7 +511,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
void updateBubble(NotificationEntry notif, boolean suppressFlyout) {
// If this is an interruptive notif, mark that it's interrupted
- if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
+ if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
notif.setInterruption();
}
mBubbleData.notificationEntryUpdated(notif, suppressFlyout);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index c9f5b79918f8..85bc22bab36f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -34,8 +34,8 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.ProximitySensor;
import java.io.PrintWriter;
@@ -51,6 +51,9 @@ import javax.inject.Singleton;
@Singleton
public class FalsingManagerProxy implements FalsingManager {
+ private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
+
+ private final ProximitySensor mProximitySensor;
private FalsingManager mInternalFalsingManager;
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
private final DeviceConfigProxy mDeviceConfig;
@@ -58,7 +61,11 @@ public class FalsingManagerProxy implements FalsingManager {
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager,
- @Named(MAIN_HANDLER_NAME) Handler handler, DeviceConfigProxy deviceConfig) {
+ @Named(MAIN_HANDLER_NAME) Handler handler,
+ ProximitySensor proximitySensor,
+ DeviceConfigProxy deviceConfig) {
+ mProximitySensor = proximitySensor;
+ mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
@@ -113,8 +120,8 @@ public class FalsingManagerProxy implements FalsingManager {
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
new FalsingDataProvider(context.getResources().getDisplayMetrics()),
- Dependency.get(AsyncSensorManager.class),
Dependency.get(KeyguardUpdateMonitor.class),
+ mProximitySensor,
mDeviceConfig
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 9e646b627bd2..3f5cae678d01 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -19,10 +19,6 @@ package com.android.systemui.classifier.brightline;
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED;
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.util.Log;
@@ -34,12 +30,11 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.ProximitySensor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
/**
* FalsingManager designed to make clear why a touch was rejected.
@@ -49,9 +44,9 @@ public class BrightLineFalsingManager implements FalsingManager {
static final boolean DEBUG = false;
private static final String TAG = "FalsingManagerPlugin";
- private final SensorManager mSensorManager;
private final FalsingDataProvider mDataProvider;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final ProximitySensor mProximitySensor;
private boolean mSessionStarted;
private MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -59,20 +54,9 @@ public class BrightLineFalsingManager implements FalsingManager {
private boolean mScreenOn;
private boolean mJustUnlockedWithFace;
- private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor();
-
private final List<FalsingClassifier> mClassifiers;
- private SensorEventListener mSensorEventListener = new SensorEventListener() {
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- onSensorEvent(event);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- };
+ private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -88,12 +72,12 @@ public class BrightLineFalsingManager implements FalsingManager {
public BrightLineFalsingManager(
FalsingDataProvider falsingDataProvider,
- SensorManager sensorManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfigProxy) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDataProvider = falsingDataProvider;
- mSensorManager = sensorManager;
+ mProximitySensor = proximitySensor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
mMetricsLogger = new MetricsLogger();
@@ -111,24 +95,12 @@ public class BrightLineFalsingManager implements FalsingManager {
}
private void registerSensors() {
- Sensor s = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- if (s != null) {
- // This can be expensive, and doesn't need to happen on the main thread.
- mBackgroundExecutor.submit(() -> {
- logDebug("registering sensor listener");
- mSensorManager.registerListener(
- mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
- });
- }
+ mProximitySensor.register(mSensorEventListener);
}
private void unregisterSensors() {
- // This can be expensive, and doesn't need to happen on the main thread.
- mBackgroundExecutor.submit(() -> {
- logDebug("unregistering sensor listener");
- mSensorManager.unregisterListener(mSensorEventListener);
- });
+ mProximitySensor.unregister(mSensorEventListener);
}
private void sessionStart() {
@@ -190,10 +162,10 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent));
}
- private void onSensorEvent(SensorEvent sensorEvent) {
+ private void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
// make these calls.
- mClassifiers.forEach((classifier) -> classifier.onSensorEvent(sensorEvent));
+ mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index 685e7c534b66..bf397518de46 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -16,10 +16,10 @@
package com.android.systemui.classifier.brightline;
-import android.hardware.SensorEvent;
import android.view.MotionEvent;
import com.android.systemui.classifier.Classifier;
+import com.android.systemui.util.ProximitySensor;
import java.util.List;
@@ -98,9 +98,9 @@ abstract class FalsingClassifier {
void onTouchEvent(MotionEvent motionEvent) {};
/**
- * Called whenever a SensorEvent occurs, specifically the ProximitySensor.
+ * Called when a ProximityEvent occurs (change in near/far).
*/
- void onSensorEvent(SensorEvent sensorEvent) {};
+ void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {};
/**
* The phone screen has turned on and we need to begin falsing detection.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index 182704726129..eeca409866a8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -19,12 +19,11 @@ package com.android.systemui.classifier.brightline;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
import android.provider.DeviceConfig;
import android.view.MotionEvent;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.ProximitySensor;
/**
@@ -99,14 +98,12 @@ class ProximityClassifier extends FalsingClassifier {
}
@Override
- public void onSensorEvent(SensorEvent sensorEvent) {
- if (sensorEvent.sensor.getType() == Sensor.TYPE_PROXIMITY) {
- logDebug("Sensor is: " + (sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange())
- + " at time " + sensorEvent.timestamp);
- update(
- sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange(),
- sensorEvent.timestamp);
- }
+ public void onProximityEvent(
+ ProximitySensor.ProximityEvent proximityEvent) {
+ boolean near = proximityEvent.getNear();
+ long timestampNs = proximityEvent.getTimestampNs();
+ logDebug("Sensor is: " + near + " at time " + timestampNs);
+ update(near, timestampNs);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 00bfb3f7cae8..bab64db4519c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -417,6 +417,9 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors.dump(pw);
}
+ /**
+ * @see DozeSensors.ProxSensor
+ */
private abstract class ProximityCheck implements SensorEventListener, Runnable {
private static final int TIMEOUT_DELAY_MS = 500;
@@ -428,6 +431,7 @@ public class DozeTriggers implements DozeMachine.Part {
private boolean mRegistered;
private boolean mFinished;
private float mMaxRange;
+ private boolean mUsingBrightnessSensor;
protected abstract void onProximityResult(int result);
@@ -435,6 +439,7 @@ public class DozeTriggers implements DozeMachine.Part {
Preconditions.checkState(!mFinished && !mRegistered);
Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
mContext.getString(R.string.doze_brightness_sensor_type));
+ mUsingBrightnessSensor = sensor != null;
if (sensor == null) {
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
}
@@ -453,6 +458,9 @@ public class DozeTriggers implements DozeMachine.Part {
mRegistered = true;
}
+ /**
+ * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
+ */
@Override
public void onSensorChanged(SensorEvent event) {
if (event.values.length == 0) {
@@ -462,7 +470,14 @@ public class DozeTriggers implements DozeMachine.Part {
if (DozeMachine.DEBUG) {
Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
}
- final boolean isNear = event.values[0] < mMaxRange;
+ final boolean isNear;
+ if (mUsingBrightnessSensor) {
+ // The custom brightness sensor is gated by the proximity sensor and will
+ // return 0 whenever prox is covered.
+ isNear = event.values[0] == 0;
+ } else {
+ isNear = event.values[0] < mMaxRange;
+ }
finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 41f66f7e2021..9221b6852112 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -155,7 +155,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
} else if (MOVE_FULL_ROWS.equals(key)) {
mFullRows = TunerService.parseIntegerSwitch(newValue, true);
} else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
- mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQs.getContext());
+ mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue);
clearAnimationState();
}
updateAnimators();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index 8c21dde8ad03..55ae61de5bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -16,17 +16,20 @@
package com.android.systemui.qs;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Dependency.BG_HANDLER;
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
@@ -52,9 +55,11 @@ public class QSCarrierGroup extends LinearLayout implements
*/
private static final int SIM_SLOTS = 3;
private final NetworkController mNetworkController;
+ private final Handler mBgHandler;
private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
+ private TextView mNoSimTextView;
private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
private CarrierTextController mCarrierTextController;
private ActivityStarter mActivityStarter;
@@ -63,17 +68,20 @@ public class QSCarrierGroup extends LinearLayout implements
@Inject
public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- NetworkController networkController, ActivityStarter activityStarter) {
+ NetworkController networkController, ActivityStarter activityStarter,
+ @Named(BG_HANDLER_NAME) Handler handler) {
super(context, attrs);
mNetworkController = networkController;
mActivityStarter = activityStarter;
+ mBgHandler = handler;
}
@VisibleForTesting
public QSCarrierGroup(Context context, AttributeSet attrs) {
this(context, attrs,
Dependency.get(NetworkController.class),
- Dependency.get(ActivityStarter.class));
+ Dependency.get(ActivityStarter.class),
+ Dependency.get(BG_HANDLER));
}
@Override
@@ -94,10 +102,13 @@ public class QSCarrierGroup extends LinearLayout implements
mCarrierDividers[0] = findViewById(R.id.qs_carrier_divider1);
mCarrierDividers[1] = findViewById(R.id.qs_carrier_divider2);
+ mNoSimTextView = findViewById(R.id.no_carrier_text);
+
for (int i = 0; i < SIM_SLOTS; i++) {
mInfos[i] = new CellSignalState();
mCarrierGroups[i].setOnClickListener(this);
}
+ mNoSimTextView.setOnClickListener(this);
CharSequence separator = mContext.getString(
com.android.internal.R.string.kg_text_message_separator);
@@ -110,8 +121,7 @@ public class QSCarrierGroup extends LinearLayout implements
return;
}
mListening = listening;
- // TODO(b/140053526)
- whitelistIpcs(this::updateListeners);
+ mBgHandler.post(this::updateListeners);
}
@Override
@@ -155,50 +165,47 @@ public class QSCarrierGroup extends LinearLayout implements
@Override
public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
- if (info.airplaneMode) {
- setVisibility(View.GONE);
- } else {
- setVisibility(View.VISIBLE);
- if (info.anySimReady) {
- boolean[] slotSeen = new boolean[SIM_SLOTS];
- if (info.listOfCarriers.length == info.subscriptionIds.length) {
- for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
- int slot = getSlotIndex(info.subscriptionIds[i]);
- if (slot >= SIM_SLOTS) {
- Log.w(TAG, "updateInfoCarrier - slot: " + slot);
- continue;
- }
- if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG,
- "Invalid SIM slot index for subscription: "
- + info.subscriptionIds[i]);
- continue;
- }
- mInfos[slot].visible = true;
- slotSeen[slot] = true;
- mCarrierGroups[slot].setCarrierText(
- info.listOfCarriers[i].toString().trim());
- mCarrierGroups[slot].setVisibility(View.VISIBLE);
+ mNoSimTextView.setVisibility(View.GONE);
+ if (!info.airplaneMode && info.anySimReady) {
+ boolean[] slotSeen = new boolean[SIM_SLOTS];
+ if (info.listOfCarriers.length == info.subscriptionIds.length) {
+ for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
+ int slot = getSlotIndex(info.subscriptionIds[i]);
+ if (slot >= SIM_SLOTS) {
+ Log.w(TAG, "updateInfoCarrier - slot: " + slot);
+ continue;
}
- for (int i = 0; i < SIM_SLOTS; i++) {
- if (!slotSeen[i]) {
- mInfos[i].visible = false;
- mCarrierGroups[i].setVisibility(View.GONE);
- }
+ if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.e(TAG,
+ "Invalid SIM slot index for subscription: "
+ + info.subscriptionIds[i]);
+ continue;
}
- } else {
- Log.e(TAG, "Carrier information arrays not of same length");
+ mInfos[slot].visible = true;
+ slotSeen[slot] = true;
+ mCarrierGroups[slot].setCarrierText(
+ info.listOfCarriers[i].toString().trim());
+ mCarrierGroups[slot].setVisibility(View.VISIBLE);
}
- } else {
- mInfos[0].visible = false;
- mCarrierGroups[0].setCarrierText(info.carrierText);
- mCarrierGroups[0].setVisibility(View.VISIBLE);
- for (int i = 1; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
- mCarrierGroups[i].setCarrierText("");
- mCarrierGroups[i].setVisibility(View.GONE);
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ if (!slotSeen[i]) {
+ mInfos[i].visible = false;
+ mCarrierGroups[i].setVisibility(View.GONE);
+ }
}
+ } else {
+ Log.e(TAG, "Carrier information arrays not of same length");
+ }
+ } else {
+ // No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
+ // info.carrierText in a different view.
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mInfos[i].visible = false;
+ mCarrierGroups[i].setCarrierText("");
+ mCarrierGroups[i].setVisibility(View.GONE);
}
+ mNoSimTextView.setText(info.carrierText);
+ mNoSimTextView.setVisibility(View.VISIBLE);
}
handleUpdateState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
new file mode 100644
index 000000000000..3f0c5bb7f620
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.qs
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import com.android.systemui.plugins.qs.QSTile
+
+private const val TAG = "QSColorController"
+private const val QS_COLOR_ICON = "qs_color_icon"
+private const val QS_COLOR_ENABLED = "qs_color_enabled"
+private const val QS_COLOR_OVERRIDDEN_TILES = "qs_color_overridden_tiles"
+private val qsColorIconUri = Settings.System.getUriFor(QS_COLOR_ICON)
+private val qsColorEnabledUri = Settings.System.getUriFor(QS_COLOR_ENABLED)
+class QSColorController private constructor() {
+
+ private var overrideColor = false
+ private var colorIcon = false
+ private lateinit var colorCache: SettingBackedMap
+
+ companion object {
+ val instance = QSColorController()
+ internal fun overrideColor() = instance.overrideColor
+ internal fun colorIcon() = instance.colorIcon
+
+ @QSTile.ColorTile
+ private fun getColorFromSetting(setting: String): Int {
+ return when (setting.toLowerCase()) {
+ "red" -> QSTile.COLOR_TILE_RED
+ "blue" -> QSTile.COLOR_TILE_BLUE
+ "green" -> QSTile.COLOR_TILE_GREEN
+ "yellow" -> QSTile.COLOR_TILE_YELLOW
+ else -> QSTile.COLOR_TILE_ACCENT
+ }
+ }
+ }
+
+ private fun getBooleanSetting(key: String, default: Boolean = false): Boolean =
+ try {
+ Settings.System.getInt(contentResolver, key) != 0
+ } catch (_: Settings.SettingNotFoundException) {
+ default
+ }
+
+ private lateinit var tileHost: QSHost
+ private lateinit var contentResolver: ContentResolver
+
+ fun initQSTileHost(host: QSHost) {
+ tileHost = host
+ contentResolver = tileHost.context.contentResolver
+ colorCache = SettingBackedMap(contentResolver, mutableMapOf())
+ colorIcon = getBooleanSetting(QS_COLOR_ICON)
+ overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
+ readExistingSettings()
+ contentResolver.registerContentObserver(qsColorEnabledUri, true, settingsListener)
+ contentResolver.registerContentObserver(qsColorIconUri, false, settingsListener)
+ }
+
+ private fun readExistingSettings() {
+ Settings.System.getString(contentResolver, QS_COLOR_OVERRIDDEN_TILES)?.split(",")
+ ?.mapNotNull { spec ->
+ Settings.System.getString(contentResolver, "$QS_COLOR_ENABLED/$spec")?.let {
+ spec to it
+ }
+ }?.forEach {
+ modifyTileColor(it.first, getColorFromSetting(it.second))
+ }
+ }
+
+ private val settingsListener = object : ContentObserver(Handler(Looper.getMainLooper())) {
+ override fun onChange(selfChange: Boolean, uri: Uri) {
+ super.onChange(selfChange, uri)
+ when (uri) {
+ qsColorIconUri -> colorIcon = getBooleanSetting(QS_COLOR_ICON)
+ qsColorEnabledUri -> overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
+ else -> {
+ uri.path?.drop("/system/".length)?.let {
+ val color = getColorFromSetting(
+ Settings.System.getString(contentResolver, it) ?: "accent")
+ val tileSpec = uri.lastPathSegment ?: ""
+ modifyTileColor(tileSpec, color)
+ }
+ }
+ }
+ }
+ }
+
+ private fun modifyTileColor(spec: String, @QSTile.ColorTile color: Int) {
+ Log.w(TAG, "Setting color of tile $spec to $color")
+ colorCache.put(spec, color)
+ tileHost.tiles.firstOrNull { it.tileSpec == spec }?.setColor(color)
+ }
+
+ fun applyColorToTile(tile: QSTile) {
+ colorCache.get(tile.tileSpec)?.let {
+ modifyTileColor(tile.tileSpec, it)
+ }
+ }
+
+ fun applyColorToAllTiles() = tileHost.tiles.forEach(::applyColorToTile)
+
+ fun destroy() {
+ contentResolver.unregisterContentObserver(settingsListener)
+ }
+
+ class SettingBackedMap(
+ private val contentResolver: ContentResolver,
+ private val map: MutableMap<String, Int>
+ ) : MutableMap<String, @QSTile.ColorTile Int> by map {
+ override fun put(key: String, @QSTile.ColorTile value: Int): Int? {
+ return map.put(key, value).also {
+ Settings.System.putString(contentResolver, QS_COLOR_OVERRIDDEN_TILES,
+ map.filterValues { it != QSTile.COLOR_TILE_ACCENT }
+ .keys
+ .joinToString(","))
+ }
+ }
+ }
+}
+fun overrideColor() = QSColorController.overrideColor()
+fun colorIcon() = QSColorController.colorIcon() \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index b597a72ba899..0a2533a8426e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -29,6 +29,11 @@ public interface QSFooter {
void setQSPanel(@Nullable QSPanel panel);
/**
+ * Sets the given {@link QuickQSPanel} to be the one associated with quick settings.
+ */
+ default void setQQSPanel(@Nullable QuickQSPanel panel) {};
+
+ /**
* Sets whether or not the footer should be visible.
*
* @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE} or {@link View#GONE}.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 644664ecaeec..0134aa3a15df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -81,6 +81,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private boolean mQsDisabled;
private QSPanel mQsPanel;
+ private QuickQSPanel mQuickQsPanel;
private boolean mExpanded;
@@ -177,7 +178,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
private void updateAnimator(int width) {
- int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
+ int numTiles = mQuickQsPanel != null ? mQuickQsPanel.getNumQuickTiles()
+ : QuickQSPanel.getDefaultMaxTiles();
int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
int remaining = (width - numTiles * size) / (numTiles - 1);
@@ -346,6 +348,11 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
@Override
+ public void setQQSPanel(@Nullable QuickQSPanel panel) {
+ mQuickQsPanel = panel;
+ }
+
+ @Override
public void onClick(View v) {
// Don't do anything until view are unhidden
if (!mExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index b27bf32b9382..61d7498ced94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -88,6 +88,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private int mCurrentUser;
private StatusBar mStatusBar;
+ private QSColorController mQSColorController = QSColorController.Companion.getInstance();
+
@Inject
public QSTileHost(Context context,
StatusBarIconController iconController,
@@ -119,6 +121,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
mAutoTiles = autoTiles.get();
});
+
+ mQSColorController.initQSTileHost(this);
}
public StatusBarIconController getIconController() {
@@ -132,6 +136,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mServices.destroy();
mPluginManager.removePluginListener(this);
mDumpController.unregisterDumpable(this);
+
+ mQSColorController.destroy();
}
@Override
@@ -275,6 +281,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mCallbacks.get(i).onTilesChanged();
}
}
+
+ mQSColorController.applyColorToAllTiles();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 1211e135d1b7..85aafa06961a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
@@ -50,9 +49,10 @@ public class QuickQSPanel extends QSPanel {
public static final String NUM_QUICK_TILES = "sysui_qqs_count";
private static final String TAG = "QuickQSPanel";
+ // Start it at 6 so a non-zero value can be obtained statically.
+ private static int sDefaultMaxTiles = 6;
private boolean mDisabledByPolicy;
- private static int mDefaultMaxTiles;
private int mMaxTiles;
protected QSPanel mFullPanel;
@@ -69,7 +69,7 @@ public class QuickQSPanel extends QSPanel {
}
removeView((View) mTileLayout);
}
- mDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+ sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
mTileLayout = new HeaderTileLayout(context);
mTileLayout.setListening(mListening);
addView((View) mTileLayout, 0 /* Between brightness and footer */);
@@ -155,14 +155,31 @@ public class QuickQSPanel extends QSPanel {
private final Tunable mNumTiles = new Tunable() {
@Override
public void onTuningChanged(String key, String newValue) {
- setMaxTiles(getNumQuickTiles(mContext));
+ setMaxTiles(parseNumTiles(newValue));
}
};
- public static int getNumQuickTiles(Context context) {
- // TODO(b/140052679)
- return whitelistIpcs(() ->
- Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, mDefaultMaxTiles));
+ public int getNumQuickTiles() {
+ return mMaxTiles;
+ }
+
+ /**
+ * Parses the String setting into the number of tiles. Defaults to {@code mDefaultMaxTiles}
+ *
+ * @param numTilesValue value of the setting to parse
+ * @return parsed value of numTilesValue OR {@code mDefaultMaxTiles} on error
+ */
+ public static int parseNumTiles(String numTilesValue) {
+ try {
+ return Integer.parseInt(numTilesValue);
+ } catch (NumberFormatException e) {
+ // Couldn't read an int from the new setting value. Use default.
+ return sDefaultMaxTiles;
+ }
+ }
+
+ public static int getDefaultMaxTiles() {
+ return sDefaultMaxTiles;
}
void setDisabledByPolicy(boolean disabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 31526bf8f5e1..88c7964144c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,10 +14,12 @@
package com.android.systemui.qs.tileimpl;
+import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -148,10 +150,15 @@ public class QSIconViewImpl extends QSIconView {
iv.clearColorFilter();
}
if (state.state != mState) {
- int color = getColor(state.state);
+ int color = getColor(state.state, state.colorActive);
mState = state.state;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
- animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
+ if (colorIcon()) {
+ animateColor(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
+ } else {
+ animateGrayScale(mTint, color, iv,
+ () -> updateIcon(iv, state, allowAnimations));
+ }
mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
@@ -168,8 +175,12 @@ public class QSIconViewImpl extends QSIconView {
}
}
+ protected int getColor(int state, int colorActive) {
+ return getColorForState(getContext(), state, colorActive);
+ }
+
protected int getColor(int state) {
- return getColorForState(getContext(), state);
+ return getColor(state, -1);
}
private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -206,6 +217,37 @@ public class QSIconViewImpl extends QSIconView {
}
}
+ private void animateColor(int fromColor, int toColor, ImageView iv,
+ final Runnable endRunnable) {
+ if (iv instanceof AlphaControlledSlashImageView) {
+ ((AlphaControlledSlashImageView) iv)
+ .setFinalImageTintList(ColorStateList.valueOf(toColor));
+ }
+ if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
+
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(QS_ANIM_LENGTH);
+ anim.addUpdateListener(animation -> {
+ float fraction = animation.getAnimatedFraction();
+ int color = (int) ArgbEvaluator.getInstance().evaluate(fraction, fromColor,
+ toColor);
+
+ setTint(iv, color);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endRunnable.run();
+ }
+ });
+ anim.start();
+ } else {
+ setTint(iv, toColor);
+ endRunnable.run();
+ }
+ }
+
public static void setTint(ImageView iv, int color) {
iv.setImageTintList(ColorStateList.valueOf(color));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index c186e59056aa..8b7f280608a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -13,6 +13,8 @@
*/
package com.android.systemui.qs.tileimpl;
+import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
+import static com.android.systemui.qs.QSColorControllerKt.overrideColor;
import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
import android.animation.ValueAnimator;
@@ -34,6 +36,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.PathParser;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -41,6 +44,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Switch;
+import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -65,12 +69,16 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private boolean mShowRippleEffect = true;
private final ImageView mBg;
+ private final TextView mDetailText;
private final int mColorActive;
private final int mColorInactive;
private final int mColorDisabled;
private int mCircleColor;
private int mBgSize;
+ private final boolean mQsColors = overrideColor();
+ private final boolean mQSIcons = colorIcon();
+
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
}
@@ -101,6 +109,12 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
mIconFrame.addView(mIcon, params);
+
+ // "..." afforadance below icon
+ mDetailText = (TextView) LayoutInflater.from(context).inflate(R.layout.qs_tile_detail_text,
+ mIconFrame, false);
+ mIconFrame.addView(mDetailText);
+
mIconFrame.setClipChildren(false);
mIconFrame.setClipToPadding(false);
@@ -156,6 +170,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
tile.longClick();
return true;
});
+
+ if (tile.supportsDetailView()) {
+ mDetailText.setVisibility(View.VISIBLE);
+ }
}
public void init(OnClickListener click, OnClickListener secondaryClick,
@@ -194,7 +212,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
protected void handleStateChanged(QSTile.State state) {
- int circleColor = getCircleColor(state.state);
+ int circleColor = getCircleColor(state.state, mQsColors ? state.colorActive : -1);
boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
if (allowAnimations) {
@@ -209,6 +227,8 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
mCircleColor = circleColor;
}
+ mDetailText.setTextColor(QSTileImpl.getColorForState(getContext(), state.state));
+
mShowRippleEffect = state.showRippleEffect;
setClickable(state.state != Tile.STATE_UNAVAILABLE);
setLongClickable(state.handlesLongClick);
@@ -239,10 +259,11 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
return mLocInScreen[1] >= -getHeight();
}
- private int getCircleColor(int state) {
+ private int getCircleColor(int state, int colorActive) {
switch (state) {
case Tile.STATE_ACTIVE:
- return mColorActive;
+ int color = (colorActive == -1) ? mColorActive : colorActive;
+ return mQsColors && mQSIcons ? Utils.applyAlpha(0.5f, color) : color;
case Tile.STATE_INACTIVE:
case Tile.STATE_UNAVAILABLE:
return mColorDisabled;
@@ -252,6 +273,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
+ private int getCircleColor(int state) {
+ return getCircleColor(state, -1);
+ }
+
@Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
@@ -342,4 +367,4 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 3d6ee4cf9617..681de378ff57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -26,6 +26,7 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
import android.app.ActivityManager;
import android.content.Context;
@@ -54,6 +55,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
+import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
@@ -98,8 +100,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
- protected TState mState = newTileState();
- private TState mTmpState = newTileState();
+ protected TState mState;
+ private TState mTmpState;
private boolean mAnnounceNextStateChange;
private String mTileSpec;
@@ -132,6 +134,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected QSTileImpl(QSHost host) {
mHost = host;
mContext = host.getContext();
+ mState = newTileState();
+ mTmpState = newTileState();
mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
}
@@ -422,6 +426,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
public abstract CharSequence getTileLabel();
public static int getColorForState(Context context, int state) {
+ return getColorForState(context, state, -1);
+ }
+
+ public static int getColorForState(Context context, int state, int colorActive) {
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
@@ -429,13 +437,25 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
case Tile.STATE_INACTIVE:
return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
case Tile.STATE_ACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
+ return getActiveColor(context, colorActive);
default:
Log.e("QSTile", "Invalid state " + state);
return 0;
}
}
+ private static int getActiveColor(Context context, int colorActive) {
+ if (colorIcon()) {
+ if (colorActive == -1) {
+ return Utils.getColorAccentDefaultColor(context);
+ } else {
+ return colorActive;
+ }
+ } else {
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
+ }
+ }
+
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
@@ -614,4 +634,27 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
pw.println(this.getClass().getSimpleName() + ":");
pw.print(" "); pw.println(getState().toString());
}
+
+ @Override
+ public void setColor(@ColorTile int color) {
+ int resId;
+ switch(color) {
+ case COLOR_TILE_RED:
+ resId = R.color.GM2_red_500;
+ break;
+ case COLOR_TILE_BLUE:
+ resId = R.color.GM2_blue_500;
+ break;
+ case COLOR_TILE_GREEN:
+ resId = R.color.GM2_green_500;
+ break;
+ case COLOR_TILE_YELLOW:
+ resId = R.color.GM2_yellow_500;
+ break;
+ default:
+ resId = -1;
+ }
+ mTmpState.colorActive = resId == -1 ? -1 : mContext.getColor(resId);
+ refreshState();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b79b662a2bd7..b7ce101cacab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -118,6 +118,11 @@ public class WifiTile extends QSTileImpl<SignalState> {
}
@Override
+ public boolean supportsDetailView() {
+ return getDetailAdapter() != null && mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK;
+ }
+
+ @Override
protected void handleClick() {
if (mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK) {
mActivityStarter.postStartActivityDismissingKeyguard(WIFI_PANEL, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 2ae2ac570b33..156e3c072dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
@@ -93,7 +91,6 @@ public class KeyguardIndicationController implements StateListener,
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTextView;
- private KeyguardIndicationTextView mDisclosure;
private final UserManager mUserManager;
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
@@ -180,7 +177,6 @@ public class KeyguardIndicationController implements StateListener,
mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
setIndicationArea(indicationArea);
- updateDisclosure();
mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
@@ -193,7 +189,6 @@ public class KeyguardIndicationController implements StateListener,
mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
mInitialTextColorState = mTextView != null ?
mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
- mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
updateIndication(false /* animate */);
}
@@ -231,27 +226,6 @@ public class KeyguardIndicationController implements StateListener,
return mUpdateMonitorCallback;
}
- private void updateDisclosure() {
- if (mDevicePolicyManager == null) {
- return;
- }
-
- // TODO(b/140053632)
- if (!mDozing && whitelistIpcs(mDevicePolicyManager::isDeviceManaged)) {
- final CharSequence organizationName =
- mDevicePolicyManager.getDeviceOwnerOrganizationName();
- if (organizationName != null) {
- mDisclosure.switchIndication(mContext.getResources().getString(
- R.string.do_disclosure_with_name, organizationName));
- } else {
- mDisclosure.switchIndication(R.string.do_disclosure_generic);
- }
- mDisclosure.setVisibility(View.VISIBLE);
- } else {
- mDisclosure.setVisibility(View.GONE);
- }
- }
-
public void setVisible(boolean visible) {
mVisible = visible;
mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -580,7 +554,6 @@ public class KeyguardIndicationController implements StateListener,
}
mDozing = dozing;
updateIndication(false);
- updateDisclosure();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -640,13 +613,6 @@ public class KeyguardIndicationController implements StateListener,
}
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- if (showing) {
- updateDisclosure();
- }
- }
-
- @Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index 0f295ba75fe4..48e2923c97d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -27,6 +27,18 @@ public interface NotificationLifetimeExtender {
boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
/**
+ * It's possible that a notification was canceled before it ever became visible. This callback
+ * gives lifetime extenders a chance to make sure it shows up. For example if a foreground
+ * service is canceled too quickly but we still want to make sure a FGS notification shows.
+ * @param pendingEntry the canceled (but pending) entry
+ * @return true if the notification lifetime should be extended
+ */
+ default boolean shouldExtendLifetimeForPendingNotification(
+ @NonNull NotificationEntry pendingEntry) {
+ return false;
+ }
+
+ /**
* Sets whether or not the lifetime should be managed by the extender. In practice, if
* shouldManage is true, this is where the extender starts managing the entry internally and is
* now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 289277e63760..f782fab7e49f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -316,7 +317,7 @@ public class NotificationLockscreenUserManagerImpl implements
boolean exceedsPriorityThreshold;
if (NotificationUtils.useNewInterruptionModel(mContext)
&& hideSilentNotificationsOnLockscreen()) {
- exceedsPriorityThreshold = entry.isTopBucket();
+ exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
} else {
exceedsPriorityThreshold =
!getEntryManager().getNotificationData().isAmbient(entry.key);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index 3db02b9743da..266fe8dc2708 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -26,8 +26,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -39,22 +37,24 @@ public class NotificationUiAdjustment {
public final String key;
public final List<Notification.Action> smartActions;
- public final CharSequence[] smartReplies;
+ public final List<CharSequence> smartReplies;
@VisibleForTesting
NotificationUiAdjustment(
- String key, List<Notification.Action> smartActions, CharSequence[] smartReplies) {
+ String key, List<Notification.Action> smartActions, List<CharSequence> smartReplies) {
this.key = key;
this.smartActions = smartActions == null
? Collections.emptyList()
- : new ArrayList<>(smartActions);
- this.smartReplies = smartReplies == null ? new CharSequence[0] : smartReplies.clone();
+ : smartActions;
+ this.smartReplies = smartReplies == null
+ ? Collections.emptyList()
+ : smartReplies;
}
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationEntry entry) {
return new NotificationUiAdjustment(
- entry.key, entry.systemGeneratedSmartActions, entry.systemGeneratedSmartReplies);
+ entry.key, entry.getSmartActions(), entry.getSmartReplies());
}
public static boolean needReinflate(
@@ -66,7 +66,7 @@ public class NotificationUiAdjustment {
if (areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions)) {
return true;
}
- if (!Arrays.equals(oldAdjustment.smartReplies, newAdjustment.smartReplies)) {
+ if (!newAdjustment.smartReplies.equals(oldAdjustment.smartReplies)) {
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 66a06193d0bf..50d9bae77bea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -436,7 +436,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
}
row.showAppOpsIcons(entry.mActiveAppOps);
- row.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs);
+ row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs());
}
Trace.beginSection("NotificationPresenter#onUpdateRowStates");
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 00321744f75a..b6b149dd049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -124,7 +124,7 @@ public class NotificationEntryManager implements
@Inject
public NotificationEntryManager(Context context) {
- mNotificationData = new NotificationData();
+ mNotificationData = new NotificationData(context);
}
/** Adds a {@link NotificationEntryListener}. */
@@ -281,10 +281,24 @@ public class NotificationEntryManager implements
}
final NotificationEntry entry = mNotificationData.get(key);
+ boolean lifetimeExtended = false;
- abortExistingInflation(key);
+ // Notification was canceled before it got inflated
+ if (entry == null) {
+ NotificationEntry pendingEntry = mPendingNotifications.get(key);
+ if (pendingEntry != null) {
+ for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+ if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
+ extendLifetime(pendingEntry, extender);
+ lifetimeExtended = true;
+ }
+ }
+ }
+ }
- boolean lifetimeExtended = false;
+ if (!lifetimeExtended) {
+ abortExistingInflation(key);
+ }
if (entry != null) {
// If a manager needs to keep the notification around for whatever reason, we
@@ -471,7 +485,7 @@ public class NotificationEntryManager implements
NotificationUiAdjustment adjustment =
NotificationUiAdjustment.extractFromNotificationEntry(entry);
oldAdjustments.put(entry.key, adjustment);
- oldImportances.put(entry.key, entry.importance);
+ oldImportances.put(entry.key, entry.getImportance());
}
// Populate notification entries from the new rankings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index f99662e69b8e..5a0b88cbdc28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -110,7 +110,7 @@ public class NotificationFilter {
return true;
}
- if (entry.suspended) {
+ if (entry.isSuspended()) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 150667b86828..9362d2d5e2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -169,7 +169,7 @@ public class NotificationInterruptionStateProvider {
return false;
}
- if (!entry.canBubble) {
+ if (!entry.canBubble()) {
if (DEBUG) {
Log.d(TAG, "No bubble up: not allowed to bubble: " + sbn.getKey());
}
@@ -244,7 +244,7 @@ public class NotificationInterruptionStateProvider {
return false;
}
- if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
}
@@ -307,7 +307,7 @@ public class NotificationInterruptionStateProvider {
return false;
}
- if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+ if (entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1af47dd0f4c0..dfbbf987dd96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -36,6 +36,7 @@ public class NotificationUtils {
private static final int[] sLocationOffset = new int[2];
@Nullable private static Boolean sUseNewInterruptionModel = null;
+ @Nullable private static Boolean sUsePeopleFiltering = null;
public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) {
Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
@@ -87,4 +88,17 @@ public class NotificationUtils {
}
return sUseNewInterruptionModel;
}
+
+ /**
+ * Caches and returns the value of the people filtering setting. Cannot change except through
+ * process restarts.
+ */
+ public static boolean usePeopleFiltering(Context context) {
+ if (sUsePeopleFiltering == null) {
+ sUsePeopleFiltering = context.getResources().getBoolean(
+ R.bool.config_usePeopleFiltering);
+ }
+
+ return sUsePeopleFiltering;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 4a6c7d27516c..6fe4abee6133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -73,7 +73,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
final boolean mAmbientStateHasChanged =
- entry.ambient != entry.getRow().isLowPriority();
+ entry.isAmbient() != entry.getRow().isLowPriority();
if (mAmbientStateHasChanged) {
mLowPriorityReorderingViews.add(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 727e245ae38b..aacb2dd0682f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -16,10 +16,15 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
+
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
+import android.content.Context;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -30,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -44,6 +50,7 @@ import java.util.Objects;
* The list of currently displaying notifications.
*/
public class NotificationData {
+ private static final String TAG = "NotificationData";
private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
@@ -64,6 +71,11 @@ public class NotificationData {
private RankingMap mRankingMap;
private final Ranking mTmpRanking = new Ranking();
+ private final boolean mUsePeopleFiltering;
+
+ public NotificationData(Context context) {
+ mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context);
+ }
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
@@ -72,52 +84,25 @@ public class NotificationData {
@VisibleForTesting
protected final Comparator<NotificationEntry> mRankingComparator =
new Comparator<NotificationEntry>() {
- private final Ranking mRankingA = new Ranking();
- private final Ranking mRankingB = new Ranking();
-
@Override
public int compare(NotificationEntry a, NotificationEntry b) {
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
- int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
- int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
- int aRank = 0;
- int bRank = 0;
-
- if (mRankingMap != null) {
- // RankingMap as received from NoMan
- getRanking(a.key, mRankingA);
- getRanking(b.key, mRankingB);
- aImportance = mRankingA.getImportance();
- bImportance = mRankingB.getImportance();
- aRank = mRankingA.getRank();
- bRank = mRankingB.getRank();
- }
+ int aRank = getRank(a.key);
+ int bRank = getRank(b.key);
- String mediaNotification = getMediaManager().getMediaNotificationKey();
+ boolean aMedia = isImportantMedia(a);
+ boolean bMedia = isImportantMedia(b);
- // IMPORTANCE_MIN media streams are allowed to drift to the bottom
- final boolean aMedia = a.key.equals(mediaNotification)
- && aImportance > NotificationManager.IMPORTANCE_MIN;
- final boolean bMedia = b.key.equals(mediaNotification)
- && bImportance > NotificationManager.IMPORTANCE_MIN;
+ boolean aSystemMax = isSystemMax(a);
+ boolean bSystemMax = isSystemMax(b);
- boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
- && isSystemNotification(na);
- boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
- && isSystemNotification(nb);
+ boolean aHeadsUp = a.isRowHeadsUp();
+ boolean bHeadsUp = b.isRowHeadsUp();
-
- boolean aHeadsUp = a.getRow().isHeadsUp();
- boolean bHeadsUp = b.getRow().isHeadsUp();
-
- // HACK: This should really go elsewhere, but it's currently not straightforward to
- // extract the comparison code and we're guaranteed to touch every element, so this is
- // the best place to set the buckets for the moment.
- a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority());
- b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority());
-
- if (aHeadsUp != bHeadsUp) {
+ if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) {
+ return a.hasAssociatedPeople() ? -1 : 1;
+ } else if (aHeadsUp != bHeadsUp) {
return aHeadsUp ? -1 : 1;
} else if (aHeadsUp) {
// Provide consistent ranking with headsUpManager
@@ -317,14 +302,6 @@ public class NotificationData {
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public int getImportance(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getImportance();
- }
- return NotificationManager.IMPORTANCE_UNSPECIFIED;
- }
-
public List<SnoozeCriterion> getSnoozeCriteria(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
@@ -349,6 +326,22 @@ public class NotificationData {
return 0;
}
+ private boolean isImportantMedia(NotificationEntry e) {
+ int importance = e.ranking().getImportance();
+ boolean media = e.key.equals(getMediaManager().getMediaNotificationKey())
+ && importance > NotificationManager.IMPORTANCE_MIN;
+
+ return media;
+ }
+
+ private boolean isSystemMax(NotificationEntry e) {
+ int importance = e.ranking().getImportance();
+ boolean sys = importance >= NotificationManager.IMPORTANCE_HIGH
+ && isSystemNotification(e.notification);
+
+ return sys;
+ }
+
public boolean shouldHide(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
@@ -414,13 +407,37 @@ public class NotificationData {
}
}
- if (mSortedAndFiltered.size() == 1) {
- // HACK: We need the comparator to run on all children in order to set the
- // isHighPriority field. If there is only one child, then the comparison won't be run,
- // so we have to trigger it manually. Get rid of this code as soon as possible.
- mRankingComparator.compare(mSortedAndFiltered.get(0), mSortedAndFiltered.get(0));
+ Collections.sort(mSortedAndFiltered, mRankingComparator);
+
+ int bucket = BUCKET_PEOPLE;
+ for (NotificationEntry e : mSortedAndFiltered) {
+ assignBucketForEntry(e);
+ if (e.getBucket() < bucket) {
+ android.util.Log.wtf(TAG, "Detected non-contiguous bucket!");
+ }
+ bucket = e.getBucket();
+ }
+ }
+
+ private void assignBucketForEntry(NotificationEntry e) {
+ boolean isHeadsUp = e.isRowHeadsUp();
+ boolean isMedia = isImportantMedia(e);
+ boolean isSystemMax = isSystemMax(e);
+
+ setBucket(e, isHeadsUp, isMedia, isSystemMax);
+ }
+
+ private void setBucket(
+ NotificationEntry e,
+ boolean isHeadsUp,
+ boolean isMedia,
+ boolean isSystemMax) {
+ if (mUsePeopleFiltering && e.hasAssociatedPeople()) {
+ e.setBucket(BUCKET_PEOPLE);
+ } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
+ e.setBucket(BUCKET_ALERTING);
} else {
- Collections.sort(mSortedAndFiltered, mRankingComparator);
+ e.setBucket(BUCKET_SILENT);
}
}
@@ -466,6 +483,19 @@ public class NotificationData {
}
/**
+ * Get the current set of buckets for notification entries, as defined here
+ */
+ public static int[] getNotificationBuckets(Context context) {
+ if (NotificationUtils.usePeopleFiltering(context)) {
+ return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT};
+ } else if (NotificationUtils.useNewInterruptionModel(context)) {
+ return new int[]{BUCKET_ALERTING, BUCKET_SILENT};
+ } else {
+ return new int[]{BUCKET_ALERTING};
+ }
+ }
+
+ /**
* Provides access to keyguard state and user settings dependent data.
*/
public interface KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 6f5fe8b629fb..a33d23c0b5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -21,6 +21,7 @@ import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.Notification.EXTRA_MESSAGES;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -29,6 +30,8 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+
import android.annotation.NonNull;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
@@ -59,9 +62,9 @@ import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -86,14 +89,12 @@ public final class NotificationEntry {
private static final long INITIALIZATION_DELAY = 400;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
+
public final String key;
public StatusBarNotification notification;
private Ranking mRanking;
- public long lastAudiblyAlertedMs;
public boolean noisy;
- public boolean ambient;
- public int importance;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
public StatusBarIconView centeredIcon;
@@ -103,14 +104,7 @@ public final class NotificationEntry {
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
- public List<SnoozeCriterion> snoozeCriteria;
- public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
- /** Smart Actions provided by the NotificationAssistantService. */
- @NonNull
- public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
- /** Smart replies provided by the NotificationAssistantService. */
- @NonNull
- public CharSequence[] systemGeneratedSmartReplies = new CharSequence[0];
+ private final List<Person> mAssociatedPeople = new ArrayList<>();
/**
* If {@link android.app.RemoteInput#getEditChoicesBeforeSending} is enabled, and the user is
@@ -119,10 +113,6 @@ public final class NotificationEntry {
*/
public EditedSuggestionInfo editedSuggestionInfo;
- @VisibleForTesting
- public int suppressedVisualEffects;
- public boolean suspended;
-
private NotificationEntry parent; // our parent (if we're in a group)
private ExpandableNotificationRow row; // the outer expanded view
@@ -151,55 +141,23 @@ public final class NotificationEntry {
private boolean hasSentReply;
/**
- * Whether this notification has been approved globally, at the app level, and at the channel
- * level for bubbling.
- */
- public boolean canBubble;
-
- /**
* Whether this notification is shown to the user as a high priority notification: visible on
* the lock screen/status bar and in the top section in the shade.
*/
private boolean mHighPriority;
- private boolean mIsTopBucket;
-
private boolean mSensitive = true;
private Runnable mOnSensitiveChangedListener;
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
+ private int mBucket = BUCKET_ALERTING;
public NotificationEntry(
- StatusBarNotification sbn,
+ @NonNull StatusBarNotification sbn,
@NonNull Ranking ranking) {
- this(sbn, ranking, false);
- }
-
- private NotificationEntry(
- StatusBarNotification sbn,
- Ranking ranking,
- boolean isTest) {
this.key = sbn.getKey();
- this.notification = sbn;
-
- // TODO: Update tests to no longer need to pass null ranking
- if (ranking != null) {
- setRanking(ranking);
- } else if (!isTest) {
- throw new IllegalArgumentException("Ranking cannot be null");
- }
- }
-
- /**
- * Method for old tests that build NotificationEntries with a ranking.
- *
- * @deprecated New tests should pass a ranking object as well.
- */
- @VisibleForTesting
- @Deprecated
- public static NotificationEntry buildForTest(StatusBarNotification sbn) {
- // TODO START here this will NPE on all tests
- return new NotificationEntry(sbn, null, true);
+ setNotification(sbn);
+ setRanking(ranking);
}
/** The key for this notification. Guaranteed to be immutable and unique */
@@ -220,11 +178,12 @@ public final class NotificationEntry {
* TODO: Make this package-private
*/
public void setNotification(StatusBarNotification sbn) {
- if (!sbn.getKey().equals(key)) {
+ if (sbn.getKey() != null && key != null && !sbn.getKey().equals(key)) {
throw new IllegalArgumentException("New key " + sbn.getKey()
+ " doesn't match existing key " + key);
}
notification = sbn;
+ updatePeopleList();
}
/**
@@ -241,27 +200,59 @@ public final class NotificationEntry {
* TODO: Make this package-private
*/
public void setRanking(@NonNull Ranking ranking) {
+ if (!ranking.getKey().equals(key)) {
+ throw new IllegalArgumentException("New key " + ranking.getKey()
+ + " doesn't match existing key " + key);
+ }
mRanking = ranking;
-
- lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
- importance = ranking.getImportance();
- ambient = ranking.isAmbient();
- snoozeCriteria = ranking.getSnoozeCriteria();
- userSentiment = ranking.getUserSentiment();
- systemGeneratedSmartActions = ranking.getSmartActions() == null
- ? Collections.emptyList() : ranking.getSmartActions();
- systemGeneratedSmartReplies = ranking.getSmartReplies() == null
- ? new CharSequence[0]
- : ranking.getSmartReplies().toArray(new CharSequence[0]);
- suppressedVisualEffects = ranking.getSuppressedVisualEffects();
- suspended = ranking.isSuspended();
- canBubble = ranking.canBubble();
}
public NotificationChannel getChannel() {
return mRanking.getChannel();
}
+ public long getLastAudiblyAlertedMs() {
+ return mRanking.getLastAudiblyAlertedMillis();
+ }
+
+ public boolean isAmbient() {
+ return mRanking.isAmbient();
+ }
+
+ public int getImportance() {
+ return mRanking.getImportance();
+ }
+
+ public List<SnoozeCriterion> getSnoozeCriteria() {
+ return mRanking.getSnoozeCriteria();
+ }
+
+ public int getUserSentiment() {
+ return mRanking.getUserSentiment();
+ }
+
+ public int getSuppressedVisualEffects() {
+ return mRanking.getSuppressedVisualEffects();
+ }
+
+ public boolean isSuspended() {
+ return mRanking.isSuspended();
+ }
+
+ /** @see Ranking#canBubble() */
+ public boolean canBubble() {
+ return mRanking.canBubble();
+ }
+
+ public @NonNull List<Notification.Action> getSmartActions() {
+ return mRanking.getSmartActions();
+ }
+
+ public @NonNull List<CharSequence> getSmartReplies() {
+ return mRanking.getSmartReplies();
+ }
+
+
public void setInterruption() {
interruption = true;
}
@@ -278,20 +269,39 @@ public final class NotificationEntry {
this.mHighPriority = highPriority;
}
- /**
- * @return True if the notif should appear in the "top" or "important" section of notifications
- * (as opposed to the "bottom" or "silent" section). This is usually the same as
- * {@link #isHighPriority()}, but there are certain exceptions, such as media notifs.
- */
- public boolean isTopBucket() {
- return mIsTopBucket;
+ public boolean isBubble() {
+ return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
}
- public void setIsTopBucket(boolean isTopBucket) {
- mIsTopBucket = isTopBucket;
+
+ private void updatePeopleList() {
+ mAssociatedPeople.clear();
+
+ Bundle extras = notification.getNotification().extras;
+ if (extras == null) {
+ return;
+ }
+
+ List<Person> p = extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST);
+
+ if (p != null) {
+ mAssociatedPeople.addAll(p);
+ }
+
+ if (Notification.MessagingStyle.class.equals(
+ notification.getNotification().getNotificationStyle())) {
+ final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+ if (!ArrayUtils.isEmpty(messages)) {
+ for (Notification.MessagingStyle.Message message :
+ Notification.MessagingStyle.Message
+ .getMessagesFromBundleArray(messages)) {
+ mAssociatedPeople.add(message.getSenderPerson());
+ }
+ }
+ }
}
- public boolean isBubble() {
- return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
+ boolean hasAssociatedPeople() {
+ return mAssociatedPeople.size() > 0;
}
/**
@@ -310,6 +320,15 @@ public final class NotificationEntry {
}
}
+ @NotificationSectionsManager.PriorityBucket
+ public int getBucket() {
+ return mBucket;
+ }
+
+ public void setBucket(@NotificationSectionsManager.PriorityBucket int bucket) {
+ mBucket = bucket;
+ }
+
public ExpandableNotificationRow getRow() {
return row;
}
@@ -788,7 +807,7 @@ public final class NotificationEntry {
if (isExemptFromDndVisualSuppression()) {
return false;
}
- return (suppressedVisualEffects & effect) != 0;
+ return (getSuppressedVisualEffects() & effect) != 0;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 247c31fc80a3..60cf995ad8d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -214,7 +214,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
// no-op here.
}
} else {
- if (oldImportance != null && entry.importance != oldImportance) {
+ if (oldImportance != null && entry.getImportance() != oldImportance) {
if (entry.rowExists()) {
entry.getRow().onNotificationRankingUpdated();
}
@@ -228,7 +228,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
PackageManager pmUser,
StatusBarNotification sbn,
ExpandableNotificationRow row) {
- row.setIsLowPriority(entry.ambient);
+ row.setIsLowPriority(entry.isAmbient());
// Extract target SDK version.
try {
@@ -248,7 +248,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
row.setOnActivatedListener(mPresenter);
boolean useIncreasedCollapsedHeight =
- mMessagingUtil.isImportantMessaging(sbn, entry.importance);
+ mMessagingUtil.isImportantMessaging(sbn, entry.getImportance());
boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
&& !mPresenter.isPresenterFullyCollapsed();
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 2c15e87ee1e6..73093c6f471f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -87,7 +87,7 @@ public class NotificationBlockingHelperManager {
// - The row is blockable (i.e. not non-blockable)
// - The dismissed row is a valid group (>1 or 0 children from the same channel)
// or the only child in the group
- if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG)
+ if ((row.getEntry().getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
&& mIsShadeExpanded
&& !row.getIsNonblockable()
&& ((!row.isChildInGroup() || row.isOnlyChildInGroup())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0c5b27b92878..cc2078bd158b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1181,7 +1181,7 @@ public class NotificationContentView extends FrameLayout {
mCurrentSmartRepliesAndActions.smartActions == null ? 0 :
mCurrentSmartRepliesAndActions.smartActions.actions.size(),
mCurrentSmartRepliesAndActions.smartReplies == null ? 0 :
- mCurrentSmartRepliesAndActions.smartReplies.choices.length));
+ mCurrentSmartRepliesAndActions.smartReplies.choices.size()));
}
applySmartReplyView(mCurrentSmartRepliesAndActions, entry);
}
@@ -1295,7 +1295,7 @@ public class NotificationContentView extends FrameLayout {
if (smartRepliesAndActions.smartReplies != null
|| smartRepliesAndActions.smartActions != null) {
int numSmartReplies = smartRepliesAndActions.smartReplies == null
- ? 0 : smartRepliesAndActions.smartReplies.choices.length;
+ ? 0 : smartRepliesAndActions.smartReplies.choices.size();
int numSmartActions = smartRepliesAndActions.smartActions == null
? 0 : smartRepliesAndActions.smartActions.actions.size();
boolean fromAssistant = smartRepliesAndActions.smartReplies == null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 3e8825d735da..9d0dd6b683cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -241,7 +241,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
notificationSnoozeView.setStatusBarNotification(sbn);
- notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
+ notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria());
guts.setHeightChangedListener((NotificationGuts g) -> {
mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
});
@@ -325,7 +325,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mDeviceProvisionedController.isDeviceProvisioned(),
row.getIsNonblockable(),
isForBlockingHelper,
- row.getEntry().importance,
+ row.getEntry().getImportance(),
row.getEntry().isHighPriority());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 422184699952..ec0c6348fd89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,11 +16,10 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
-
-
+import android.content.Context;
import android.util.MathUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,11 +50,14 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
private float mAppearFraction;
@Inject
- NotificationRoundnessManager(KeyguardBypassController keyguardBypassController) {
- mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
- mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
- mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
- mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
+ NotificationRoundnessManager(
+ KeyguardBypassController keyguardBypassController,
+ Context context) {
+ int numberOfSections = NotificationData.getNotificationBuckets(context).length;
+ mFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
+ mLastInSectionViews = new ActivatableNotificationView[numberOfSections];
+ mTmpFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
+ mTmpLastInSectionViews = new ActivatableNotificationView[numberOfSections];
mBypassController = keyguardBypassController;
}
@@ -157,7 +159,7 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
public void updateRoundedChildren(NotificationSection[] sections) {
boolean anyChanged = false;
- for (int i = 0; i < NUM_SECTIONS; i++) {
+ for (int i = 0; i < sections.length; i++) {
mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
mTmpLastInSectionViews[i] = mLastInSectionViews[i];
mFirstInSectionViews[i] = sections[i].getFirstVisibleChild();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
index f39ed2e89432..9d456ef785a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
* bounds change.
*/
class NotificationSection {
+ private @NotificationSectionsManager.PriorityBucket int mBucket;
private View mOwningView;
private Rect mBounds = new Rect();
private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
@@ -43,8 +44,9 @@ class NotificationSection {
private ActivatableNotificationView mFirstVisibleChild;
private ActivatableNotificationView mLastVisibleChild;
- NotificationSection(View owningView) {
+ NotificationSection(View owningView, @NotificationSectionsManager.PriorityBucket int bucket) {
mOwningView = owningView;
+ mBucket = bucket;
}
public void cancelAnimators() {
@@ -72,6 +74,11 @@ class NotificationSection {
return mBottomAnimator != null || mTopAnimator != null;
}
+ @NotificationSectionsManager.PriorityBucket
+ public int getBucket() {
+ return mBucket;
+ }
+
public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) {
// Left and right bounds are always applied immediately.
mCurrentBounds.left = mBounds.left;
@@ -199,12 +206,16 @@ class NotificationSection {
return mLastVisibleChild;
}
- public void setFirstVisibleChild(ActivatableNotificationView child) {
+ public boolean setFirstVisibleChild(ActivatableNotificationView child) {
+ boolean changed = mFirstVisibleChild != child;
mFirstVisibleChild = child;
+ return changed;
}
- public void setLastVisibleChild(ActivatableNotificationView child) {
+ public boolean setLastVisibleChild(ActivatableNotificationView child) {
+ boolean changed = mLastVisibleChild != child;
mLastVisibleChild = child;
+ return changed;
}
public void resetCurrentBounds() {
@@ -291,5 +302,4 @@ class NotificationSection {
mBounds.bottom = bottom;
return bottom;
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index d119fb79e4c6..d0444ae81b89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -18,6 +18,10 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.provider.Settings;
@@ -34,23 +38,31 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Manages the boundaries of the two notification sections (high priority and low priority). Also
* shows/hides the headers for those sections where appropriate.
*
* TODO: Move remaining sections logic from NSSL into this class.
*/
-class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider {
+public class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider {
+
+ private static final String TAG = "NotifSectionsManager";
+ private static final boolean DEBUG = false;
+
private final NotificationStackScrollLayout mParent;
private final ActivityStarter mActivityStarter;
private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
- private final boolean mUseMultipleSections;
+ private final int mNumberOfSections;
private boolean mInitialized = false;
private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible = false;
- @Nullable private ExpandableNotificationRow mFirstGentleNotif;
+
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
NotificationSectionsManager(
@@ -58,12 +70,21 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
- boolean useMultipleSections) {
+ int numberOfSections) {
mParent = parent;
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
- mUseMultipleSections = useMultipleSections;
+ mNumberOfSections = numberOfSections;
+ }
+
+ NotificationSection[] createSectionsForBuckets(int[] buckets) {
+ NotificationSection[] sections = new NotificationSection[buckets.length];
+ for (int i = 0; i < buckets.length; i++) {
+ sections[i] = new NotificationSection(mParent, buckets[i] /* bucket */);
+ }
+
+ return sections;
}
/** Must be called before use. */
@@ -111,8 +132,38 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
}
@Override
- public boolean beginsSection(View view) {
- return view == getFirstLowPriorityChild();
+ public boolean beginsSection(@NonNull View view, @Nullable View previous) {
+ boolean begin = false;
+ if (view instanceof ExpandableNotificationRow) {
+ if (previous instanceof ExpandableNotificationRow) {
+ // If we're drawing the first non-person notification, break out a section
+ ExpandableNotificationRow curr = (ExpandableNotificationRow) view;
+ ExpandableNotificationRow prev = (ExpandableNotificationRow) previous;
+
+ begin = curr.getEntry().getBucket() != prev.getEntry().getBucket();
+ }
+ }
+
+ if (!begin) {
+ begin = view == mGentleHeader;
+ }
+
+ return begin;
+ }
+
+ private boolean isUsingMultipleSections() {
+ return mNumberOfSections > 1;
+ }
+
+ private @PriorityBucket int getBucket(ActivatableNotificationView view)
+ throws IllegalArgumentException {
+ if (view instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) view).getEntry().getBucket();
+ } else if (view == mGentleHeader) {
+ return BUCKET_SILENT;
+ }
+
+ throw new IllegalArgumentException("I don't know how to find a bucket for this view :(");
}
/**
@@ -120,11 +171,10 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
* bookkeeping and adds/moves/removes section headers if appropriate.
*/
void updateSectionBoundaries() {
- if (!mUseMultipleSections) {
+ if (!isUsingMultipleSections()) {
return;
}
- mFirstGentleNotif = null;
int firstGentleNotifIndex = -1;
final int n = mParent.getChildCount();
@@ -133,9 +183,8 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
if (child instanceof ExpandableNotificationRow
&& child.getVisibility() != View.GONE) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!row.getEntry().isTopBucket()) {
+ if (row.getEntry().getBucket() == BUCKET_SILENT) {
firstGentleNotifIndex = i;
- mFirstGentleNotif = row;
break;
}
}
@@ -184,78 +233,73 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
}
/**
- * Updates the boundaries (as tracked by their first and last views) of the high and low
- * priority sections.
+ * Updates the boundaries (as tracked by their first and last views) of the priority sections.
*
* @return {@code true} If the last view in the top section changed (so we need to animate).
*/
- boolean updateFirstAndLastViewsInSections(
- final NotificationSection highPrioritySection,
- final NotificationSection lowPrioritySection,
- ActivatableNotificationView firstChild,
- ActivatableNotificationView lastChild) {
- if (mUseMultipleSections) {
- ActivatableNotificationView previousLastHighPriorityChild =
- highPrioritySection.getLastVisibleChild();
- ActivatableNotificationView previousFirstLowPriorityChild =
- lowPrioritySection.getFirstVisibleChild();
- ActivatableNotificationView lastHighPriorityChild = getLastHighPriorityChild();
- ActivatableNotificationView firstLowPriorityChild = getFirstLowPriorityChild();
- if (lastHighPriorityChild != null && firstLowPriorityChild != null) {
- highPrioritySection.setFirstVisibleChild(firstChild);
- highPrioritySection.setLastVisibleChild(lastHighPriorityChild);
- lowPrioritySection.setFirstVisibleChild(firstLowPriorityChild);
- lowPrioritySection.setLastVisibleChild(lastChild);
- } else if (lastHighPriorityChild != null) {
- highPrioritySection.setFirstVisibleChild(firstChild);
- highPrioritySection.setLastVisibleChild(lastChild);
- lowPrioritySection.setFirstVisibleChild(null);
- lowPrioritySection.setLastVisibleChild(null);
- } else {
- highPrioritySection.setFirstVisibleChild(null);
- highPrioritySection.setLastVisibleChild(null);
- lowPrioritySection.setFirstVisibleChild(firstChild);
- lowPrioritySection.setLastVisibleChild(lastChild);
+ boolean updateFirstAndLastViewsForAllSections(
+ NotificationSection[] sections,
+ List<ActivatableNotificationView> children) {
+
+ if (sections.length <= 0 || children.size() <= 0) {
+ for (NotificationSection s : sections) {
+ s.setFirstVisibleChild(null);
+ s.setLastVisibleChild(null);
}
- return lastHighPriorityChild != previousLastHighPriorityChild
- || firstLowPriorityChild != previousFirstLowPriorityChild;
- } else {
- highPrioritySection.setFirstVisibleChild(firstChild);
- highPrioritySection.setLastVisibleChild(lastChild);
return false;
}
- }
- @VisibleForTesting
- SectionHeaderView getGentleHeaderView() {
- return mGentleHeader;
- }
+ boolean changed = false;
+ ArrayList<ActivatableNotificationView> viewsInBucket = new ArrayList<>();
+ for (NotificationSection s : sections) {
+ int filter = s.getBucket();
+ viewsInBucket.clear();
- @Nullable
- private ActivatableNotificationView getFirstLowPriorityChild() {
- if (mGentleHeaderVisible) {
- return mGentleHeader;
- } else {
- return mFirstGentleNotif;
- }
- }
+ //TODO: do this in a single pass, and more better
+ for (ActivatableNotificationView v : children) {
+ if (getBucket(v) == filter) {
+ viewsInBucket.add(v);
+ }
- @Nullable
- private ActivatableNotificationView getLastHighPriorityChild() {
- ActivatableNotificationView lastChildBeforeGap = null;
- int childCount = mParent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mParent.getChildAt(i);
- if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!row.getEntry().isTopBucket()) {
- break;
+ if (viewsInBucket.size() >= 1) {
+ changed |= s.setFirstVisibleChild(viewsInBucket.get(0));
+ changed |= s.setLastVisibleChild(viewsInBucket.get(viewsInBucket.size() - 1));
} else {
- lastChildBeforeGap = row;
+ changed |= s.setFirstVisibleChild(null);
+ changed |= s.setLastVisibleChild(null);
}
}
}
- return lastChildBeforeGap;
+
+ if (DEBUG) {
+ logSections(sections);
+ }
+
+ return changed;
+ }
+
+ private void logSections(NotificationSection[] sections) {
+ for (int i = 0; i < sections.length; i++) {
+ NotificationSection s = sections[i];
+ ActivatableNotificationView first = s.getFirstVisibleChild();
+ String fs = first == null ? "(null)"
+ : (first instanceof ExpandableNotificationRow)
+ ? ((ExpandableNotificationRow) first).getEntry().key
+ : Integer.toHexString(System.identityHashCode(first));
+ ActivatableNotificationView last = s.getLastVisibleChild();
+ String ls = last == null ? "(null)"
+ : (last instanceof ExpandableNotificationRow)
+ ? ((ExpandableNotificationRow) last).getEntry().key
+ : Integer.toHexString(System.identityHashCode(last));
+ android.util.Log.d(TAG, "updateSections: f=" + fs + " s=" + i);
+ android.util.Log.d(TAG, "updateSections: l=" + ls + " s=" + i);
+ }
+ }
+
+
+ @VisibleForTesting
+ SectionHeaderView getGentleHeaderView() {
+ return mGentleHeader;
}
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@@ -279,4 +323,20 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
mOnClearGentleNotifsClickListener.onClick(v);
}
}
+
+ /**
+ * For now, declare the available notification buckets (sections) here so that other
+ * presentation code can decide what to do based on an entry's buckets
+ *
+ */
+ @Retention(SOURCE)
+ @IntDef(prefix = { "BUCKET_" }, value = {
+ BUCKET_PEOPLE,
+ BUCKET_ALERTING,
+ BUCKET_SILENT
+ })
+ public @interface PriorityBucket {}
+ public static final int BUCKET_PEOPLE = 0;
+ public static final int BUCKET_ALERTING = 1;
+ public static final int BUCKET_SILENT = 2;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6a611a623c69..a67018ef9710 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
@@ -112,6 +113,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -172,7 +174,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
*/
private static final int INVALID_POINTER = -1;
- static final int NUM_SECTIONS = 2;
/**
* The distance in pixels between sections when the sections are directly adjacent (no visible
* gap is drawn between them). In this case we don't want to round their corners.
@@ -351,7 +352,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return true;
}
};
- private NotificationSection[] mSections = new NotificationSection[NUM_SECTIONS];
+ private NotificationSection[] mSections;
private boolean mAnimateNextBackgroundTop;
private boolean mAnimateNextBackgroundBottom;
private boolean mAnimateNextSectionBoundsChange;
@@ -522,9 +523,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mAllowLongPress = allowLongPress;
- for (int i = 0; i < NUM_SECTIONS; i++) {
- mSections[i] = new NotificationSection(this);
- }
mRoundnessManager = notificationRoundnessManager;
mHeadsUpManager = headsUpManager;
@@ -533,19 +531,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mKeyguardBypassController = keyguardBypassController;
mFalsingManager = falsingManager;
+ int[] buckets = NotificationData.getNotificationBuckets(context);
mSectionsManager =
new NotificationSectionsManager(
this,
activityStarter,
statusBarStateController,
configurationController,
- NotificationUtils.useNewInterruptionModel(context));
+ buckets.length);
mSectionsManager.initialize(LayoutInflater.from(context));
mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
// Leave the shade open if there will be other notifs left over to clear
final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
clearNotifications(ROWS_GENTLE, closeShade);
});
+ mSections = mSectionsManager.createSectionsForBuckets(buckets);
mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
mBgColor = context.getColor(R.color.notification_shade_background_color);
@@ -773,7 +773,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground
&& (mSections[0].getCurrentBounds().top
- < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
+ < mSections[mSections.length - 1].getCurrentBounds().bottom
|| mAmbientState.isDozing())) {
drawBackground(canvas);
} else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
@@ -819,7 +819,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
int lockScreenLeft = mSidePaddings;
int lockScreenRight = getWidth() - mSidePaddings;
int lockScreenTop = mSections[0].getCurrentBounds().top;
- int lockScreenBottom = mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom;
+ int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom;
int hiddenLeft = getWidth() / 2;
int hiddenTop = mTopPadding;
@@ -2636,6 +2636,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return null;
}
+ //TODO: We shouldn't have to generate this list every time
+ private List<ActivatableNotificationView> getChildrenWithBackground() {
+ ArrayList<ActivatableNotificationView> children = new ArrayList<>();
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
+ && child != mShelf) {
+ children.add((ActivatableNotificationView) child);
+ }
+ }
+
+ return children;
+ }
+
/**
* Fling the scroll view
*
@@ -3198,8 +3213,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
ActivatableNotificationView firstChild = getFirstChildWithBackground();
ActivatableNotificationView lastChild = getLastChildWithBackground();
- boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsInSections(
- mSections[0], mSections[1], firstChild, lastChild);
+ boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections(
+ mSections, getChildrenWithBackground());
if (mAnimationsEnabled && mIsExpanded) {
mAnimateNextBackgroundTop = firstChild != previousFirstChild;
@@ -5780,9 +5795,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
- beforeSpeedBump = row.getEntry().isTopBucket();
+ beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
} else {
- beforeSpeedBump = !row.getEntry().ambient;
+ beforeSpeedBump = !row.getEntry().isAmbient();
}
if (beforeSpeedBump) {
speedBumpIndex = currentIndex;
@@ -5838,9 +5853,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
case ROWS_ALL:
return true;
case ROWS_HIGH_PRIORITY:
- return row.getEntry().isTopBucket();
+ return row.getEntry().getBucket() < BUCKET_SILENT;
case ROWS_GENTLE:
- return !row.getEntry().isTopBucket();
+ return row.getEntry().getBucket() == BUCKET_SILENT;
default:
throw new IllegalArgumentException("Unknown selection: " + selection);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ef8048487022..4b61064f4a54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
@@ -411,8 +413,10 @@ public class StackScrollAlgorithm {
float currentYPosition,
boolean reverse) {
ExpandableView child = algorithmState.visibleChildren.get(i);
+ ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
final boolean applyGapHeight =
- childNeedsGapHeight(ambientState.getSectionProvider(), algorithmState, i, child);
+ childNeedsGapHeight(
+ ambientState.getSectionProvider(), algorithmState, i, child, previousChild);
ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
@@ -477,8 +481,11 @@ public class StackScrollAlgorithm {
SectionProvider sectionProvider,
StackScrollAlgorithmState algorithmState,
int visibleIndex,
- View child) {
- boolean needsGapHeight = sectionProvider.beginsSection(child) && visibleIndex > 0;
+ View child,
+ View previousChild) {
+
+ boolean needsGapHeight = sectionProvider.beginsSection(child, previousChild)
+ && visibleIndex > 0;
if (ANCHOR_SCROLLING) {
needsGapHeight &= visibleIndex != algorithmState.anchorViewIndex;
}
@@ -749,6 +756,6 @@ public class StackScrollAlgorithm {
* True if this view starts a new "section" of notifications, such as the gentle
* notifications section. False if sections are not enabled.
*/
- boolean beginsSection(View view);
+ boolean beginsSection(@NonNull View view, @Nullable View previous);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index b6f82a1b4382..83ecb55960cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -120,7 +120,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ViewGroup mIndicationArea;
- private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer;
@@ -234,8 +233,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
- mEnterpriseDisclosure = findViewById(
- R.id.keyguard_indication_enterprise_disclosure);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
@@ -312,9 +309,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
// Respect font size setting.
- mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material));
mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(
com.android.internal.R.dimen.text_size_small_material));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 5001006d1aa6..d7f67cef033e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -93,7 +93,6 @@ public class KeyguardBouncer {
private int mBouncerPromptReason;
private boolean mIsAnimatingAway;
private boolean mIsScrimmed;
- private ViewGroup mLockIconContainer;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -190,10 +189,6 @@ public class KeyguardBouncer {
return mIsScrimmed;
}
- public ViewGroup getLockIconContainer() {
- return mRoot == null || mRoot.getVisibility() != View.VISIBLE ? null : mLockIconContainer;
- }
-
/**
* This method must be called at the end of the bouncer animation when
* the translation is performed manually by the user, otherwise FalsingManager
@@ -374,11 +369,6 @@ public class KeyguardBouncer {
private void showPrimarySecurityScreen() {
mKeyguardView.showPrimarySecurityScreen();
- KeyguardSecurityView keyguardSecurityView = mKeyguardView.getCurrentSecurityView();
- if (keyguardSecurityView != null) {
- mLockIconContainer = ((ViewGroup) keyguardSecurityView)
- .findViewById(R.id.lock_icon_container);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 62d855dfd14c..67810738b17e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -105,6 +105,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private boolean mShowingLaunchAffordance;
private boolean mKeyguardJustShown;
private boolean mUpdatePending;
+ private boolean mBouncerPreHideAnimation;
private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
new KeyguardMonitor.Callback() {
@@ -125,9 +126,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
@Override
public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardMonitor.isKeyguardFadingAway() && mBlockUpdates) {
- mBlockUpdates = false;
- update(true /* force */);
+ if (!mKeyguardMonitor.isKeyguardFadingAway()) {
+ mBouncerPreHideAnimation = false;
+ if (mBlockUpdates) {
+ mBlockUpdates = false;
+ update(true /* force */);
+ }
}
}
};
@@ -464,6 +468,14 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
}
}
+ /**
+ * Animate padlock opening when bouncer challenge is solved.
+ */
+ public void onBouncerPreHideAnimation() {
+ mBouncerPreHideAnimation = true;
+ update();
+ }
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({ERROR, UNLOCK, LOCK, SCANNING})
@interface LockAnimIndex {}
@@ -511,7 +523,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private int getState() {
KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing
+ if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing || mBouncerPreHideAnimation
|| mKeyguardMonitor.isKeyguardGoingAway()) && !mSimLocked) {
return STATE_LOCK_OPEN;
} else if (mTransientBiometricsError) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a79ecd905403..ea113dffa658 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -102,6 +102,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -177,12 +178,22 @@ public class NotificationPanelView extends PanelView implements
@VisibleForTesting
final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onBiometricAuthenticated(int userId,
+ BiometricSourceType biometricSourceType) {
+ if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ mDelayShowingKeyguardStatusBar = true;
+ }
+ }
+
@Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
boolean keyguardOrShadeLocked = mBarState == StatusBarState.KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED;
- if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing) {
+ if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
+ && !mDelayShowingKeyguardStatusBar) {
mFirstBypassAttempt = false;
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
}
@@ -191,6 +202,17 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onFinishedGoingToSleep(int why) {
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ mDelayShowingKeyguardStatusBar = false;
+ }
+ };
+ private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
+ new KeyguardMonitor.Callback() {
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardMonitor.isKeyguardFadingAway()) {
+ mFirstBypassAttempt = false;
+ mDelayShowingKeyguardStatusBar = false;
+ }
}
};
@@ -413,7 +435,17 @@ public class NotificationPanelView extends PanelView implements
private boolean mShowingKeyguardHeadsUp;
private boolean mAllowExpandForSmallExpansion;
private Runnable mExpandAfterLayoutRunnable;
+
+ /**
+ * If face auth with bypass is running for the first time after you turn on the screen.
+ * (From aod or screen off)
+ */
private boolean mFirstBypassAttempt;
+ /**
+ * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
+ * the keyguard is dismissed to show the status bar.
+ */
+ private boolean mDelayShowingKeyguardStatusBar;
private PluginManager mPluginManager;
private FrameLayout mPluginFrame;
@@ -450,6 +482,7 @@ public class NotificationPanelView extends PanelView implements
mKeyguardBypassController = bypassController;
mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+ mKeyguardMonitor.addCallback(mKeyguardMonitorCallback);
dynamicPrivacyController.addListener(this);
mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
@@ -2391,7 +2424,8 @@ public class NotificationPanelView extends PanelView implements
* mKeyguardStatusBarAnimateAlpha;
newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
mKeyguardStatusBar.setAlpha(newAlpha);
- boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace();
+ boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
+ || mDelayShowingKeyguardStatusBar;
mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing && !hideForBypass
? VISIBLE : INVISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8c95b844ae2f..f853b638db46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -503,8 +503,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
* device is dozing when the light sensor is on.
*/
public void setAodFrontScrimAlpha(float alpha) {
- if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()
- && mInFrontAlpha != alpha) {
+ if (((mState == ScrimState.AOD && mDozeParameters.getAlwaysOn())
+ || mState == ScrimState.PULSING) && mInFrontAlpha != alpha) {
mInFrontAlpha = alpha;
updateScrims();
}
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 6b12c6158948..7bc849d28c53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4539,6 +4539,14 @@ public class StatusBar extends SystemUI implements DemoMode,
}
/**
+ * When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
+ */
+ public void onBouncerPreHideAnimation() {
+ mNotificationPanel.onBouncerPreHideAnimation();
+ mStatusBarWindow.onBouncerPreHideAnimation();
+ }
+
+ /**
* @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
* return PackageManager for mContext
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 50c356139fd3..bb8ba055276a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -279,14 +279,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
0 /* delay */);
} else {
final long duration;
+ final int delay;
if (needsBypassFading()) {
duration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+ delay = 0;
} else {
duration = AppearAnimationUtils.DEFAULT_APPEAR_DURATION / 2;
+ delay = 120;
}
- CrossFadeHelper.fadeOut(mLockIconContainer,
- duration /* duration */,
- 0 /* delay */, null /* runnable */);
+ CrossFadeHelper.fadeOut(mLockIconContainer, duration, delay, null /* runnable */);
}
}
}
@@ -516,7 +517,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void startPreHideAnimation(Runnable finishRunnable) {
if (mBouncer.isShowing()) {
mBouncer.startPreHideAnimation(finishRunnable);
- mNotificationPanelView.onBouncerPreHideAnimation();
+ mStatusBar.onBouncerPreHideAnimation();
} else if (finishRunnable != null) {
finishRunnable.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index bec53a1b2d3c..320243b3ee22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -411,7 +411,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
}
- } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+ } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index d8b223960953..4732049d635e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -224,7 +224,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mVisualStabilityManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(this,
notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
- // ForegroundServiceControllerListener adds its listener in its constructor
+ // ForegroundServiceNotificationListener adds its listener in its constructor
// but we need to request it here in order for it to be instantiated.
// TODO: figure out how to do this correctly once Dependency.get() is gone.
Dependency.get(ForegroundServiceNotificationListener.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 7a81ed419fff..a9e818df6bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -527,6 +527,15 @@ public class StatusBarWindowView extends FrameLayout {
}
}
+ /**
+ * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+ */
+ public void onBouncerPreHideAnimation() {
+ if (mLockIcon != null) {
+ mLockIcon.onBouncerPreHideAnimation();
+ }
+ }
+
public class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
index ee78a723a49c..25a32b330f7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -123,7 +123,7 @@ public class InflatedSmartReplies {
if (left == right) return true;
if (left == null || right == null) return false;
- if (!Arrays.equals(left.getSmartReplies(), right.getSmartReplies())) {
+ if (!left.getSmartReplies().equals(right.getSmartReplies())) {
return false;
}
@@ -199,7 +199,7 @@ public class InflatedSmartReplies {
SmartReplyView.SmartActions smartActions = null;
if (appGeneratedSmartRepliesExist) {
smartReplies = new SmartReplyView.SmartReplies(
- remoteInputActionPair.first.getChoices(),
+ Arrays.asList(remoteInputActionPair.first.getChoices()),
remoteInputActionPair.first,
remoteInputActionPair.second.actionIntent,
false /* fromAssistant */);
@@ -210,22 +210,22 @@ public class InflatedSmartReplies {
}
// Apps didn't provide any smart replies / actions, use those from NAS (if any).
if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
- boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.systemGeneratedSmartReplies)
+ boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.getSmartReplies())
&& freeformRemoteInputActionPair != null
&& freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
&& freeformRemoteInputActionPair.second.actionIntent != null;
if (useGeneratedReplies) {
smartReplies = new SmartReplyView.SmartReplies(
- entry.systemGeneratedSmartReplies,
+ entry.getSmartReplies(),
freeformRemoteInputActionPair.first,
freeformRemoteInputActionPair.second.actionIntent,
true /* fromAssistant */);
}
- boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+ boolean useSmartActions = !ArrayUtils.isEmpty(entry.getSmartActions())
&& notification.getAllowSystemGeneratedContextualActions();
if (useSmartActions) {
List<Notification.Action> systemGeneratedActions =
- entry.systemGeneratedSmartActions;
+ entry.getSmartActions();
// Filter actions if we're in kiosk-mode - we don't care about screen pinning mode,
// since notifications aren't shown there anyway.
ActivityManagerWrapper activityManagerWrapper =
@@ -288,8 +288,8 @@ public class InflatedSmartReplies {
this.smartActions = smartActions;
}
- @NonNull public CharSequence[] getSmartReplies() {
- return smartReplies == null ? new CharSequence[0] : smartReplies.choices;
+ @NonNull public List<CharSequence> getSmartReplies() {
+ return smartReplies == null ? Collections.emptyList() : smartReplies.choices;
}
@NonNull public List<Notification.Action> getSmartActions() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 282d28c3b20c..4fdaa6300065 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -220,7 +220,7 @@ public class SmartReplyView extends ViewGroup {
if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
if (smartReplies.choices != null) {
- for (int i = 0; i < smartReplies.choices.length; ++i) {
+ for (int i = 0; i < smartReplies.choices.size(); ++i) {
buttons.add(inflateReplyButton(
this, getContext(), i, smartReplies, smartReplyController, entry,
delayOnClickListener));
@@ -265,7 +265,7 @@ public class SmartReplyView extends ViewGroup {
NotificationEntry entry, boolean useDelayedOnClickListener) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, smartReplyView, false);
- CharSequence choice = smartReplies.choices[replyIndex];
+ CharSequence choice = smartReplies.choices.get(replyIndex);
b.setText(choice);
OnDismissAction action = () -> {
@@ -944,10 +944,10 @@ public class SmartReplyView extends ViewGroup {
@NonNull
public final PendingIntent pendingIntent;
@NonNull
- public final CharSequence[] choices;
+ public final List<CharSequence> choices;
public final boolean fromAssistant;
- public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+ public SmartReplies(List<CharSequence> choices, RemoteInput remoteInput,
PendingIntent pendingIntent, boolean fromAssistant) {
this.choices = choices;
this.remoteInput = remoteInput;
diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
new file mode 100644
index 000000000000..a905eba1f0ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Simple wrapper around SensorManager customized for the Proximity sensor.
+ */
+public class ProximitySensor {
+ private static final String TAG = "ProxSensor";
+ private static final boolean DEBUG = false;
+
+ private final Sensor mSensor;
+ private final AsyncSensorManager mSensorManager;
+ private final boolean mUsingBrightnessSensor;
+ private final float mMaxRange;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+ private boolean mNear;
+ private List<ProximitySensorListener> mListeners = new ArrayList<>();
+ private String mTag = null;
+
+ @Inject
+ public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
+ mSensorManager = sensorManager;
+ Sensor sensor = findBrightnessSensor(context, sensorManager);
+
+ if (sensor == null) {
+ mUsingBrightnessSensor = false;
+ sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ } else {
+ mUsingBrightnessSensor = true;
+ }
+ mSensor = sensor;
+ if (mSensor != null) {
+ mMaxRange = mSensor.getMaximumRange();
+ } else {
+ mMaxRange = 0;
+ }
+ }
+
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) {
+ String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+ List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+
+ /**
+ * Returns {@code false} if a Proximity sensor is not available.
+ */
+ public boolean getSensorAvailable() {
+ return mSensor != null;
+ }
+
+ /**
+ * Add a listener.
+ *
+ * Registers itself with the {@link SensorManager} if this is the first listener
+ * added.
+ */
+ public boolean register(ProximitySensorListener listener) {
+ if (!getSensorAvailable()) {
+ return false;
+ }
+
+ logDebug("using brightness sensor? " + mUsingBrightnessSensor);
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ logDebug("registering sensor listener");
+ mSensorManager.registerListener(
+ mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * If all listeners are removed from an instance of this class,
+ * it will unregister itself with the SensorManager.
+ */
+ public void unregister(ProximitySensorListener listener) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ logDebug("unregistering sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ }
+ }
+
+ public boolean isNear() {
+ return getSensorAvailable() && mNear;
+ }
+
+ private void onSensorEvent(SensorEvent event) {
+ boolean near = event.values[0] < mMaxRange;
+ if (mUsingBrightnessSensor) {
+ near = event.values[0] == 0;
+ }
+ mNear = near;
+ mListeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onProximitySensorEvent(
+ new ProximityEvent(mNear, event.timestamp)));
+ }
+
+ /** Implement to be notified of ProximityEvents. */
+ public interface ProximitySensorListener {
+ /** Called when the ProximitySensor changes. */
+ void onProximitySensorEvent(ProximityEvent proximityEvent);
+ }
+
+ /**
+ * Returned when the near/far state of a {@link ProximitySensor} changes.
+ */
+ public static class ProximityEvent {
+ private final boolean mNear;
+ private final long mTimestampNs;
+
+ public ProximityEvent(boolean near, long timestampNs) {
+ mNear = near;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getNear() {
+ return mNear;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 6d433d2c0770..2e94c7c12cd8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -22,11 +22,14 @@ import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -34,15 +37,18 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.text.TextUtils;
import com.android.internal.telephony.IccCardConstants;
import com.android.systemui.Dependency;
@@ -76,6 +82,9 @@ public class CarrierTextControllerTest extends SysuiTestCase {
TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
TEST_CARRIER_ID, 0);
+ private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0,
+ TEST_CARRIER, null, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
+ null, null, null, null, false, null, "");
private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
DATA_ROAMING_ENABLE, null, null, null, null, false, null, "");
@@ -281,6 +290,65 @@ public class CarrierTextControllerTest extends SysuiTestCase {
}
@Test
+ public void testCarrierText_noTextOnReadySimWhenNull() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_NULL);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
+ when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertTrue("Carrier text should be empty, instead it's " + captor.getValue().carrierText,
+ TextUtils.isEmpty(captor.getValue().carrierText));
+ assertFalse("No SIM should be available", captor.getValue().anySimReady);
+ }
+
+ @Test
+ public void testCarrierText_noTextOnReadySimWhenNull_airplaneMode_wifiOn() {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_NULL);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
+ when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
+ mockWifi();
+
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+ ServiceState ss = mock(ServiceState.class);
+ when(ss.getDataRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ mKeyguardUpdateMonitor.mServiceStates.put(TEST_SUBSCRIPTION_NULL.getSubscriptionId(), ss);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertFalse("No SIM should be available", captor.getValue().anySimReady);
+ // There's no airplane mode if at least one SIM is State.READY and there's wifi
+ assertFalse("Device should not be in airplane mode", captor.getValue().airplaneMode);
+ assertNotEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText);
+ }
+
+ private void mockWifi() {
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.getBSSID()).thenReturn("");
+ when(mWifiManager.getConnectionInfo()).thenReturn(wifiInfo);
+ }
+
+ @Test
public void testCreateInfo_noSubscriptions() {
reset(mCarrierTextCallback);
when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index ccd2138ced54..5c4ef18b61a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -392,19 +393,27 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
}
private void entryRemoved(StatusBarNotification notification) {
- mEntryListener.onEntryRemoved(NotificationEntry.buildForTest(notification),
- null, false);
+ mEntryListener.onEntryRemoved(
+ new NotificationEntryBuilder()
+ .setSbn(notification)
+ .build(),
+ null,
+ false);
}
private void entryAdded(StatusBarNotification notification, int importance) {
- NotificationEntry entry = NotificationEntry.buildForTest(notification);
- entry.importance = importance;
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(notification)
+ .setImportance(importance)
+ .build();
mEntryListener.onPendingEntryAdded(entry);
}
private void entryUpdated(StatusBarNotification notification, int importance) {
- NotificationEntry entry = NotificationEntry.buildForTest(notification);
- entry.importance = importance;
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(notification)
+ .setImportance(importance)
+ .build();
mEntryListener.onPostEntryUpdated(entry);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
new file mode 100644
index 000000000000..212c93dc576d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Notification;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
+ private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
+ private NotificationEntry mEntry;
+ private Notification mNotif;
+
+ @Before
+ public void setup() {
+ mNotif = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text")
+ .build();
+
+ mEntry = new NotificationEntryBuilder()
+ .setNotification(mNotif)
+ .build();
+ }
+
+ /**
+ * ForegroundServiceLifetimeExtenderTest
+ */
+ @Test
+ public void testShouldExtendLifetime_should_foreground() {
+ // Extend the lifetime of a FGS notification iff it has not been visible
+ // for the minimum time
+ mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ modifySbn(mEntry)
+ .setPostTime(System.currentTimeMillis())
+ .build();
+ assertTrue(mExtender.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testShouldExtendLifetime_shouldNot_foreground() {
+ mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ modifySbn(mEntry)
+ .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
+ .build();
+ assertFalse(mExtender.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testShouldExtendLifetime_shouldNot_notForeground() {
+ mNotif.flags = 0;
+ modifySbn(mEntry)
+ .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
+ .build();
+ assertFalse(mExtender.shouldExtendLifetime(mEntry));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index 6db5ef92c12c..9c920f52d56a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import org.junit.After;
import org.junit.Before;
@@ -51,6 +52,9 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.EnumMap;
+import java.util.Map;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -64,7 +68,12 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private AssistUtils mMockAssistUtils;
@Mock private Handler mMockHandler;
@Mock private PhenotypeHelper mMockPhenotypeHelper;
- @Mock private AssistHandleBehaviorController.BehaviorController mMockBehaviorController;
+ @Mock private AssistHandleOffBehavior mMockOffBehavior;
+ @Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior;
+ @Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior;
+ @Mock private AssistHandleBehaviorController.BehaviorController mMockTestBehavior;
+ @Mock private NavigationModeController mMockNavigationModeController;
+ @Mock private DumpController mMockDumpController;
@Before
public void setup() {
@@ -76,13 +85,23 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
doAnswer(answerVoid(Runnable::run)).when(mMockHandler)
.postDelayed(any(Runnable.class), anyLong());
+ Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController> behaviorMap =
+ new EnumMap<>(AssistHandleBehavior.class);
+ behaviorMap.put(AssistHandleBehavior.OFF, mMockOffBehavior);
+ behaviorMap.put(AssistHandleBehavior.LIKE_HOME, mMockLikeHomeBehavior);
+ behaviorMap.put(AssistHandleBehavior.REMINDER_EXP, mMockReminderExpBehavior);
+ behaviorMap.put(AssistHandleBehavior.TEST, mMockTestBehavior);
+
mAssistHandleBehaviorController =
new AssistHandleBehaviorController(
mContext,
mMockAssistUtils,
- mMockHandler, () -> mMockScreenDecorations,
+ mMockHandler,
+ () -> mMockScreenDecorations,
mMockPhenotypeHelper,
- mMockBehaviorController);
+ behaviorMap,
+ mMockNavigationModeController,
+ mMockDumpController);
}
@After
@@ -316,8 +335,8 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST);
// Assert
- verify(mMockBehaviorController).onModeActivated(mContext, mAssistHandleBehaviorController);
- verifyNoMoreInteractions(mMockBehaviorController);
+ verify(mMockTestBehavior).onModeActivated(mContext, mAssistHandleBehaviorController);
+ verifyNoMoreInteractions(mMockTestBehavior);
}
@Test
@@ -326,14 +345,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST);
mAssistHandleBehaviorController.setInGesturalModeForTest(true);
- reset(mMockBehaviorController);
+ reset(mMockTestBehavior);
// Act
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.OFF);
// Assert
- verify(mMockBehaviorController).onModeDeactivated();
- verifyNoMoreInteractions(mMockBehaviorController);
+ verify(mMockTestBehavior).onModeDeactivated();
+ verifyNoMoreInteractions(mMockTestBehavior);
}
@Test
@@ -346,6 +365,6 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST);
// Assert
- verifyNoMoreInteractions(mMockBehaviorController);
+ verifyNoMoreInteractions(mMockTestBehavior);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
new file mode 100644
index 000000000000..c53289c62d94
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.assist;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
+
+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
+public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
+
+ private AssistHandleLikeHomeBehavior mAssistHandleLikeHomeBehavior;
+
+ @Mock private WakefulnessLifecycle mMockWakefulnessLifecycle;
+ @Mock private SysUiState mMockSysUiState;
+ @Mock private AssistHandleCallbacks mMockAssistHandleCallbacks;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mAssistHandleLikeHomeBehavior = new AssistHandleLikeHomeBehavior(
+ () -> mMockWakefulnessLifecycle, () -> mMockSysUiState);
+ }
+
+ @Test
+ public void onModeActivated_beginsObserving() {
+ // Arrange
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockWakefulnessLifecycle).getWakefulness();
+ verify(mMockWakefulnessLifecycle).addObserver(any(WakefulnessLifecycle.Observer.class));
+ verify(mMockSysUiState).addCallback(any(SysUiState.SysUiStateCallback.class));
+ verifyNoMoreInteractions(mMockWakefulnessLifecycle, mMockSysUiState);
+ }
+
+ @Test
+ public void onModeActivated_showsHandlesWhenAwake() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).showAndStay();
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onModeActivated_hidesHandlesWhenNotAwake() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onModeDeactivated_stopsObserving() {
+ // Arrange
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
+ ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
+ ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
+ ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
+ verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
+ verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeDeactivated();
+
+ // Assert
+ verify(mMockWakefulnessLifecycle).removeObserver(eq(observer.getValue()));
+ verify(mMockSysUiState).removeCallback(eq(sysUiStateCallback.getValue()));
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onAssistantGesturePerformed_doesNothing() {
+ // Arrange
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onAssistantGesturePerformed();
+
+ // Assert
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onAssistHandlesRequested_doesNothing() {
+ // Arrange
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onAssistHandlesRequested();
+
+ // Assert
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onWake_handlesShow() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
+ ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
+ ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ observer.getValue().onStartedWakingUp();
+
+ // Assert
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ observer.getValue().onFinishedWakingUp();
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).showAndStay();
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onSleep_handlesHide() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
+ ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ observer.getValue().onStartedGoingToSleep();
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ observer.getValue().onFinishedGoingToSleep();
+
+ // Assert
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onHomeHandleHide_handlesHide() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
+ ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ sysUiStateCallback.getValue().onSystemUiStateChanged(
+ QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onHomeHandleUnhide_handlesShow() {
+ // Arrange
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
+ ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
+ sysUiStateCallback.getValue().onSystemUiStateChanged(
+ QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN);
+ reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+
+ // Act
+ sysUiStateCallback.getValue().onSystemUiStateChanged(
+ ~QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).showAndStay();
+ verifyNoMoreInteractions(
+ mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java
new file mode 100644
index 000000000000..15d4d5b4792f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.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.assist;
+
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AssistHandleOffBehaviorTest extends SysuiTestCase {
+
+ private AssistHandleOffBehavior mAssistHandleOffBehavior;
+
+ @Mock private AssistHandleCallbacks mMockAssistHandleCallbacks;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mAssistHandleOffBehavior = new AssistHandleOffBehavior();
+ }
+
+ @Test
+ public void onModeActivated_hidesHandles() {
+ // Arrange
+
+ // Act
+ mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onModeDeactivated_doesNothing() {
+ // Arrange
+ mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ reset(mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleOffBehavior.onModeDeactivated();
+
+ // Assert
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onAssistantGesturePerformed_doesNothing() {
+ // Arrange
+ mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ reset(mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleOffBehavior.onAssistantGesturePerformed();
+
+ // Assert
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
+ public void onAssistHandlesRequested_doesNothing() {
+ // Arrange
+ mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ reset(mMockAssistHandleCallbacks);
+
+ // Act
+ mAssistHandleOffBehavior.onAssistHandlesRequested();
+
+ // Assert
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index fc1870738375..7a09137b1ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -164,6 +164,17 @@ public class AuthBiometricViewTest extends SysuiTestCase {
}
@Test
+ public void testError_sendsActionError() {
+ initDialog(mContext, mCallback, new MockInjector());
+ final String testError = "testError";
+ mBiometricView.onError(testError);
+ waitForIdleSync();
+
+ verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_ERROR);
+ assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
+ }
+
+ @Test
public void testBackgroundClicked_sendsActionUserCanceled() {
initDialog(mContext, mCallback, new MockInjector());
@@ -255,7 +266,9 @@ public class AuthBiometricViewTest extends SysuiTestCase {
assertEquals(View.VISIBLE, tryAgainButton.getVisibility());
assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
- assertEquals(failureMessage, mBiometricView.mIndicatorView.getText());
+
+ // TODO: Test restored text. Currently cannot test this, since it gets restored only after
+ // dialog size is known.
}
private Bundle buildBiometricPromptBundle() {
@@ -320,6 +333,11 @@ public class AuthBiometricViewTest extends SysuiTestCase {
public ImageView getIconView() {
return mIconView;
}
+
+ @Override
+ public int getDelayAfterError() {
+ return 0; // Keep this at 0 for tests to invoke callback immediately.
+ }
}
private class TestableBiometricView extends AuthBiometricView {
@@ -347,5 +365,10 @@ public class AuthBiometricViewTest extends SysuiTestCase {
protected void handleResetAfterHelp() {
}
+
+ @Override
+ protected boolean supportsSmallDialog() {
+ return false;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 25e27ef6b781..d4fc3f842e9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -78,6 +78,13 @@ public class AuthContainerViewTest extends SysuiTestCase {
verify(mCallback).onTryAgainPressed();
}
+ @Test
+ public void testActionError_sendsDismissedError() {
+ mAuthContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_ERROR);
+ verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR);
+ }
+
private class TestableAuthContainer extends AuthContainerView {
TestableAuthContainer(AuthContainerView.Config config) {
super(config);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java
deleted file mode 100644
index 3ff1f383fdee..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogViewTest.java
+++ /dev/null
@@ -1,201 +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.biometrics;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.spy;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class BiometricDialogViewTest extends SysuiTestCase {
-
- FaceDialogView mFaceDialogView;
-
- private static final String TITLE = "Title";
- private static final String SUBTITLE = "Subtitle";
- private static final String DESCRIPTION = "Description";
- private static final String NEGATIVE_BUTTON = "Negative Button";
-
- private static final String TEST_HELP = "Help";
-
- TestableContext mTestableContext;
- @Mock
- private AuthDialogCallback mCallback;
- @Mock
- private UserManager mUserManager;
- @Mock
- private DevicePolicyManager mDpm;
-
- private static class Injector extends BiometricDialogView.Injector {
- @Override
- public WakefulnessLifecycle getWakefulnessLifecycle() {
- final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle();
- lifecycle.dispatchFinishedWakingUp();
- return lifecycle;
- }
- }
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTestableContext = spy(mContext);
- mTestableContext.addMockSystemService(UserManager.class, mUserManager);
- mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm);
- }
-
- @Test
- public void testContentStates_confirmationRequired_authenticated() {
- mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
- true /* requireConfirmation */);
- mFaceDialogView.onAttachedToWindow();
-
- // When starting authentication
- assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility());
- assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
- assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
- assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
- assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
- assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility());
- assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
-
- // Contents are as expected
- assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText()));
- assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText()));
- assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText()));
- assertTrue(mFaceDialogView.mPositiveButton.getText().toString()
- .contentEquals(mContext.getString(R.string.biometric_dialog_confirm)));
- assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText()));
- assertTrue(mFaceDialogView.mTryAgainButton.getText().toString()
- .contentEquals(mContext.getString(R.string.biometric_dialog_try_again)));
-
- // When help message is received
- mFaceDialogView.onHelp(TEST_HELP);
- assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
- assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
-
- // When authenticated, confirm button comes out
- mFaceDialogView.onAuthenticationSucceeded();
- assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
- assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled());
- }
-
- @Test
- public void testContentStates_confirmationNotRequired_authenticated() {
- mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
- false /* requireConfirmation */);
- mFaceDialogView.onAttachedToWindow();
- mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL);
-
- assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility());
- assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
- assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
- assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
- assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility());
- assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
- assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
- }
-
- @Test
- public void testContentStates_confirmationNotRequired_help() {
- mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
- false /* requireConfirmation */);
- mFaceDialogView.onAttachedToWindow();
-
- mFaceDialogView.onHelp(TEST_HELP);
- assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
- assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
- }
-
- @Test
- public void testBack_sendsUserCanceled() {
- // TODO: Need robolectric framework to wait for handler to complete
- }
-
- @Test
- public void testScreenOff_sendsUserCanceled() {
- // TODO: Need robolectric framework to wait for handler to complete
- }
-
- @Test
- public void testRestoreState_contentStatesCorrect() {
- mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
- false /* requireConfirmation */);
- mFaceDialogView.onAttachedToWindow();
- mFaceDialogView.onAuthenticationFailed(TEST_HELP);
-
- final Bundle bundle = new Bundle();
- mFaceDialogView.onSaveState(bundle);
-
- mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
- false /* requireConfirmation */);
- mFaceDialogView.restoreState(bundle);
- mFaceDialogView.onAttachedToWindow();
-
- assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility());
- }
-
- private FaceDialogView buildFaceDialogView(Context context, AuthDialogCallback callback,
- boolean requireConfirmation) {
- return (FaceDialogView) new BiometricDialogView.Builder(context)
- .setCallback(callback)
- .setBiometricPromptBundle(createTestDialogBundle())
- .setRequireConfirmation(requireConfirmation)
- .setUserId(0)
- .setOpPackageName("test_package")
- .build(BiometricDialogView.Builder.TYPE_FACE, new Injector());
- }
-
- private Bundle createTestDialogBundle() {
- Bundle bundle = new Bundle();
-
- bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE);
- bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE);
- bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION);
- bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON);
-
- // RequireConfirmation is a hint to BiometricService. This can be forced to be required
- // by user settings, and should be tested in BiometricService.
- bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
-
- return bundle;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 238cfd78bfaf..392a7cbc4d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleData.TimeSource;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -878,7 +879,7 @@ public class BubbleDataTest extends SysuiTestCase {
when(sbn.getNotification()).thenReturn(notification);
// NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
- return NotificationEntry.buildForTest(sbn);
+ return new NotificationEntryBuilder().setSbn(sbn).build();
}
private void setCurrentTime(long time) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 04cf4bb4c94b..57578611e3f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -19,19 +19,16 @@ package com.android.systemui.bubbles;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
import android.app.Notification;
import android.os.Bundle;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
@@ -45,8 +42,6 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
public class BubbleTest extends SysuiTestCase {
@Mock
- private StatusBarNotification mStatusBarNotification;
- @Mock
private Notification mNotif;
private NotificationEntry mEntry;
@@ -57,28 +52,16 @@ public class BubbleTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mStatusBarNotification.getKey()).thenReturn("key");
- when(mStatusBarNotification.getNotification()).thenReturn(mNotif);
- when(mStatusBarNotification.getUser()).thenReturn(new UserHandle(0));
mExtras = new Bundle();
mNotif.extras = mExtras;
- mEntry = NotificationEntry.buildForTest(mStatusBarNotification);
+ mEntry = new NotificationEntryBuilder()
+ .setNotification(mNotif)
+ .build();
mBubble = new Bubble(mContext, mEntry);
}
@Test
- public void testInitialization() {
- final Ranking ranking = new Ranking();
-
- final NotificationEntry entry = new NotificationEntry(mStatusBarNotification, ranking);
-
- assertEquals("key", entry.key());
- assertEquals(mStatusBarNotification, entry.sbn());
- assertEquals(ranking, entry.ranking());
- }
-
- @Test
public void testGetUpdateMessage_default() {
final String msg = "Hello there!";
doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index 1fbb44346a84..ae4581a80503 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -76,6 +76,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
}
@Test
+ @Ignore
public void testExpansionAndCollapse() throws InterruptedException {
Runnable afterExpand = Mockito.mock(Runnable.class);
mExpandedController.expandFromStack(afterExpand);
@@ -93,6 +94,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
}
@Test
+ @Ignore
public void testOnChildAdded() throws InterruptedException {
expand();
@@ -105,6 +107,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
}
@Test
+ @Ignore
public void testOnChildRemoved() throws InterruptedException {
expand();
@@ -116,6 +119,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
}
@Test
+ @Ignore
public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
expand();
@@ -133,6 +137,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
}
@Test
+ @Ignore
public void testBubbleDismissed() throws InterruptedException {
expand();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
index 86554b1d71aa..498330c83dcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
@@ -38,6 +38,7 @@ import androidx.test.filters.SmallTest;
import com.google.android.collect.Sets;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -76,6 +77,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testHierarchyChanges() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -101,6 +103,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testUpdateValueNotChained() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -127,11 +130,13 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testUpdateValueXChained() throws InterruptedException {
testChainedTranslationAnimations();
}
@Test
+ @Ignore
public void testSetEndActions() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -175,6 +180,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testRemoveEndListeners() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -213,6 +219,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testSetController() throws InterruptedException {
// Add the bubbles, then set the controller, to make sure that a controller added to an
// already-initialized view works correctly.
@@ -269,6 +276,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testArePropertiesAnimating() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -293,6 +301,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testCancelAllAnimations() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -346,6 +355,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testPhysicsAnimator() throws InterruptedException {
mLayout.setActiveController(mTestableController);
addOneMoreThanBubbleLimitBubbles();
@@ -390,6 +400,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testAnimationsForChildrenFromIndex() throws InterruptedException {
// Don't chain since we're going to invoke each animation independently.
mTestableController.setChainedProperties(new HashSet<>());
@@ -415,6 +426,7 @@ public class PhysicsAnimationLayoutTest extends PhysicsAnimationLayoutTestCase {
}
@Test
+ @Ignore
public void testAnimationsForChildrenFromIndex_noChildren() {
mLayout.setActiveController(mTestableController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index e1457ef28e36..b9793562d8d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -34,6 +34,7 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.ProximitySensor;
import org.junit.After;
import org.junit.Before;
@@ -46,8 +47,10 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class FalsingManagerProxyTest extends SysuiTestCase {
- @Mock
+ @Mock(stubOnly = true)
PluginManager mPluginManager;
+ @Mock(stubOnly = true)
+ ProximitySensor mProximitySensor;
private Handler mHandler;
private FalsingManagerProxy mProxy;
private DeviceConfigProxy mDeviceConfig;
@@ -73,7 +76,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Test
public void test_brightLineFalsingManagerDisabled() {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig);
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
+ mDeviceConfig);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -82,13 +86,15 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
mTestableLooper.processAllMessages();
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig);
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
+ mDeviceConfig);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig);
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
+ mDeviceConfig);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index c76fe74b1af7..3fc5d7202d0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -23,8 +23,6 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
@@ -32,17 +30,15 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.ProximitySensor;
import org.junit.After;
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.lang.reflect.Field;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -81,8 +77,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testPass_mostlyUncovered() {
touchDown();
- mClassifier.onSensorEvent(createSensorEvent(true, 1));
- mClassifier.onSensorEvent(createSensorEvent(false, 2));
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 2));
touchUp(20);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -91,8 +87,8 @@ public class ProximityClassifierTest extends ClassifierTest {
public void testPass_quickSettings() {
touchDown();
when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
- mClassifier.onSensorEvent(createSensorEvent(true, 1));
- mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -100,8 +96,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testFail_covered() {
touchDown();
- mClassifier.onSensorEvent(createSensorEvent(true, 1));
- mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -109,10 +105,10 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testFail_mostlyCovered() {
touchDown();
- mClassifier.onSensorEvent(createSensorEvent(true, 1));
- mClassifier.onSensorEvent(createSensorEvent(true, 95));
- mClassifier.onSensorEvent(createSensorEvent(true, 96));
- mClassifier.onSensorEvent(createSensorEvent(false, 100));
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(true, 95));
+ mClassifier.onProximityEvent(createSensorEvent(true, 96));
+ mClassifier.onProximityEvent(createSensorEvent(false, 100));
touchUp(100);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -120,8 +116,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testPass_coveredWithLongSwipe() {
touchDown();
- mClassifier.onSensorEvent(createSensorEvent(true, 1));
- mClassifier.onSensorEvent(createSensorEvent(false, 11));
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
when(mDistanceClassifier.isLongSwipe()).thenReturn(true);
assertThat(mClassifier.isFalseTouch(), is(false));
@@ -142,26 +138,7 @@ public class ProximityClassifierTest extends ClassifierTest {
motionEvent.recycle();
}
- private SensorEvent createSensorEvent(boolean covered, long timestampMs) {
- SensorEvent sensorEvent = Mockito.mock(SensorEvent.class);
- Sensor sensor = Mockito.mock(Sensor.class);
- when(sensor.getType()).thenReturn(Sensor.TYPE_PROXIMITY);
- when(sensor.getMaximumRange()).thenReturn(1f);
- sensorEvent.sensor = sensor;
- sensorEvent.timestamp = timestampMs * NS_PER_MS;
- try {
- Field valuesField = SensorEvent.class.getField("values");
- valuesField.setAccessible(true);
- float[] sensorValue = {covered ? 0 : 1};
- try {
- valuesField.set(sensorEvent, sensorValue);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- }
-
- return sensorEvent;
+ private ProximitySensor.ProximityEvent createSensorEvent(boolean covered, long timestampMs) {
+ return new ProximitySensor.ProximityEvent(covered, timestampMs * NS_PER_MS);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index e6f36e6abbfd..cf5a12fcc2af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -129,7 +129,9 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
public void setUp() {
mTestHandler = Handler.createAsync(Looper.myLooper());
mSbn = createNewNotification(0 /* id */);
- mEntry = NotificationEntry.buildForTest(mSbn);
+ mEntry = new NotificationEntryBuilder()
+ .setSbn(mSbn)
+ .build();
mEntry.setRow(mRow);
mAlertingNotificationManager = createAlertingNotificationManager();
@@ -180,7 +182,9 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
public void testReleaseAllImmediately() {
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .build();
entry.setRow(mRow);
mAlertingNotificationManager.showNotification(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 2427cfc77f39..0817ee908184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -46,7 +45,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -68,17 +66,11 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class KeyguardIndicationControllerTest extends SysuiTestCase {
- private final String ORGANIZATION_NAME = "organization";
-
- private String mDisclosureWithOrganization;
-
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private ViewGroup mIndicationArea;
@Mock
- private KeyguardIndicationTextView mDisclosure;
- @Mock
private LockIcon mLockIcon;
@Mock
private LockPatternUtils mLockPatternUtils;
@@ -107,11 +99,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
- mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
- ORGANIZATION_NAME);
- when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
- .thenReturn(mDisclosure);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
mWakeLock = new WakeLockFake();
@@ -127,72 +115,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
- public void unmanaged() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
- createController();
-
- verify(mDisclosure).setVisibility(View.GONE);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void managedNoOwnerName() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- createController();
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void managedOwnerName() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
- createController();
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void updateOnTheFly() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
- createController();
-
- final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
- reset(mDisclosure);
-
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- monitor.onKeyguardVisibilityChanged(true);
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- reset(mDisclosure);
-
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
- monitor.onKeyguardVisibilityChanged(false);
- monitor.onKeyguardVisibilityChanged(true);
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
- verifyNoMoreInteractions(mDisclosure);
- reset(mDisclosure);
-
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
- monitor.onKeyguardVisibilityChanged(false);
- monitor.onKeyguardVisibilityChanged(true);
-
- verify(mDisclosure).setVisibility(View.GONE);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
public void transientIndication_holdsWakeLock_whenDozing() {
createController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
new file mode 100644
index 000000000000..fcfdd11a1906
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java
@@ -0,0 +1,209 @@
+/*
+ * 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.statusbar;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager.Importance;
+import android.os.UserHandle;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.ArrayList;
+
+/**
+ * Combined builder for constructing a NotificationEntry and its associated StatusBarNotification
+ * and Ranking. Is largely a proxy for the SBN and Ranking builders, but does a little extra magic
+ * to make sure the keys match between the two, etc.
+ *
+ * Only for use in tests.
+ */
+public class NotificationEntryBuilder {
+ private final SbnBuilder mSbnBuilder = new SbnBuilder();
+ private final RankingBuilder mRankingBuilder = new RankingBuilder();
+ private StatusBarNotification mSbn = null;
+
+ public NotificationEntry build() {
+ StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
+ mRankingBuilder.setKey(sbn.getKey());
+ return new NotificationEntry(sbn, mRankingBuilder.build());
+ }
+
+ /**
+ * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
+ * ignored.
+ */
+ public NotificationEntryBuilder setSbn(@Nullable StatusBarNotification sbn) {
+ mSbn = sbn;
+ return this;
+ }
+
+ /* Delegated to SbnBuilder */
+
+ public NotificationEntryBuilder setPkg(String pkg) {
+ mSbnBuilder.setPkg(pkg);
+ return this;
+ }
+
+ public NotificationEntryBuilder setOpPkg(String opPkg) {
+ mSbnBuilder.setOpPkg(opPkg);
+ return this;
+ }
+
+ public NotificationEntryBuilder setId(int id) {
+ mSbnBuilder.setId(id);
+ return this;
+ }
+
+ public NotificationEntryBuilder setTag(String tag) {
+ mSbnBuilder.setTag(tag);
+ return this;
+ }
+
+ public NotificationEntryBuilder setUid(int uid) {
+ mSbnBuilder.setUid(uid);
+ return this;
+ }
+
+ public NotificationEntryBuilder setInitialPid(int initialPid) {
+ mSbnBuilder.setInitialPid(initialPid);
+ return this;
+ }
+
+ public NotificationEntryBuilder setNotification(Notification notification) {
+ mSbnBuilder.setNotification(notification);
+ return this;
+ }
+
+ public NotificationEntryBuilder setUser(UserHandle user) {
+ mSbnBuilder.setUser(user);
+ return this;
+ }
+
+ public NotificationEntryBuilder setOverrideGroupKey(String overrideGroupKey) {
+ mSbnBuilder.setOverrideGroupKey(overrideGroupKey);
+ return this;
+ }
+
+ public NotificationEntryBuilder setPostTime(long postTime) {
+ mSbnBuilder.setPostTime(postTime);
+ return this;
+ }
+
+ /* Delegated to RankingBuilder */
+
+ public NotificationEntryBuilder setRank(int rank) {
+ mRankingBuilder.setRank(rank);
+ return this;
+ }
+
+ public NotificationEntryBuilder setMatchesInterruptionFilter(
+ boolean matchesInterruptionFilter) {
+ mRankingBuilder.setMatchesInterruptionFilter(matchesInterruptionFilter);
+ return this;
+ }
+
+ public NotificationEntryBuilder setVisibilityOverride(int visibilityOverride) {
+ mRankingBuilder.setVisibilityOverride(visibilityOverride);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSuppressedVisualEffects(int suppressedVisualEffects) {
+ mRankingBuilder.setSuppressedVisualEffects(suppressedVisualEffects);
+ return this;
+ }
+
+ public NotificationEntryBuilder setExplanation(CharSequence explanation) {
+ mRankingBuilder.setExplanation(explanation);
+ return this;
+ }
+
+ public NotificationEntryBuilder setAdditionalPeople(ArrayList<String> additionalPeople) {
+ mRankingBuilder.setAdditionalPeople(additionalPeople);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSnoozeCriteria(
+ ArrayList<SnoozeCriterion> snoozeCriteria) {
+ mRankingBuilder.setSnoozeCriteria(snoozeCriteria);
+ return this;
+ }
+
+ public NotificationEntryBuilder setCanShowBadge(boolean canShowBadge) {
+ mRankingBuilder.setCanShowBadge(canShowBadge);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSuspended(boolean suspended) {
+ mRankingBuilder.setSuspended(suspended);
+ return this;
+ }
+
+ public NotificationEntryBuilder setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
+ mRankingBuilder.setLastAudiblyAlertedMs(lastAudiblyAlertedMs);
+ return this;
+ }
+
+ public NotificationEntryBuilder setNoisy(boolean noisy) {
+ mRankingBuilder.setNoisy(noisy);
+ return this;
+ }
+
+ public NotificationEntryBuilder setCanBubble(boolean canBubble) {
+ mRankingBuilder.setCanBubble(canBubble);
+ return this;
+ }
+
+ public NotificationEntryBuilder setImportance(@Importance int importance) {
+ mRankingBuilder.setImportance(importance);
+ return this;
+ }
+
+ public NotificationEntryBuilder setUserSentiment(int userSentiment) {
+ mRankingBuilder.setUserSentiment(userSentiment);
+ return this;
+ }
+
+ public NotificationEntryBuilder setChannel(NotificationChannel channel) {
+ mRankingBuilder.setChannel(channel);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSmartActions(
+ ArrayList<Notification.Action> smartActions) {
+ mRankingBuilder.setSmartActions(smartActions);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSmartActions(Notification.Action... smartActions) {
+ mRankingBuilder.setSmartActions(smartActions);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSmartReplies(ArrayList<CharSequence> smartReplies) {
+ mRankingBuilder.setSmartReplies(smartReplies);
+ return this;
+ }
+
+ public NotificationEntryBuilder setSmartReplies(CharSequence... smartReplies) {
+ mRankingBuilder.setSmartReplies(smartReplies);
+ return this;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
new file mode 100644
index 000000000000..33b0d2c32ba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
@@ -0,0 +1,66 @@
+/*
+ * 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.statusbar;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+@VisibleForTesting
+public class NotificationEntryHelper {
+ public static ModifiedRankingBuilder modifyRanking(NotificationEntry entry) {
+ return new ModifiedRankingBuilder(entry);
+ }
+
+ public static ModifiedSbnBuilder modifySbn(NotificationEntry entry) {
+ return new ModifiedSbnBuilder(entry);
+ }
+
+ public static class ModifiedRankingBuilder extends RankingBuilder {
+ private final NotificationEntry mTarget;
+
+ private ModifiedRankingBuilder(NotificationEntry target) {
+ super(target.ranking());
+ mTarget = target;
+ }
+
+ @Override
+ public Ranking build() {
+ final Ranking ranking = super.build();
+ mTarget.setRanking(ranking);
+ return ranking;
+ }
+ }
+
+ public static class ModifiedSbnBuilder extends SbnBuilder {
+ private final NotificationEntry mTarget;
+
+ private ModifiedSbnBuilder(NotificationEntry target) {
+ super(target.sbn());
+ mTarget = target;
+ }
+
+ @Override
+ public StatusBarNotification build() {
+ final StatusBarNotification sbn = super.build();
+ mTarget.setNotification(sbn);
+ return sbn;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index b044595e5a8e..350ab5afdf95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -23,6 +23,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.google.common.truth.Truth.assertThat;
@@ -40,9 +41,7 @@ import android.graphics.drawable.Icon;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.service.dreams.IDreamManager;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -231,15 +230,17 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
public void testCanAlertCommon_false_suppressedForGroups() {
ensureStateForAlertCommon();
- Notification n = new Notification.Builder(getContext(), "a")
- .setGroup("a")
- .setGroupSummary(true)
- .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(new Notification.Builder(getContext(), "a")
+ .setGroup("a")
+ .setGroupSummary(true)
+ .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
+ .build())
+ .setImportance(IMPORTANCE_DEFAULT)
.build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.importance = IMPORTANCE_DEFAULT;
assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
}
@@ -288,7 +289,9 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
ensureStateForHeadsUpWhenDozing();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- entry.suppressedVisualEffects = SUPPRESSED_EFFECT_AMBIENT;
+ modifyRanking(entry)
+ .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT)
+ .build();
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@@ -379,7 +382,9 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+ modifyRanking(entry)
+ .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
+ .build();
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@@ -481,7 +486,9 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
ensureStateForBubbleUp();
NotificationEntry entry = createBubble();
- entry.canBubble = false;
+ modifyRanking(entry)
+ .setCanBubble(false)
+ .build();
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
@@ -494,7 +501,9 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- entry.canBubble = true;
+ modifyRanking(entry)
+ .setCanBubble(true)
+ .build();
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
@@ -507,8 +516,10 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- entry.canBubble = true;
- entry.notification.getNotification().flags |= FLAG_BUBBLE;
+ modifyRanking(entry)
+ .setCanBubble(true)
+ .build();
+ entry.sbn().getNotification().flags |= FLAG_BUBBLE;
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
@@ -549,13 +560,16 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
.setContentText("content text")
.setBubbleMetadata(data)
.build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.notification.getNotification().flags |= FLAG_BUBBLE;
- entry.importance = IMPORTANCE_HIGH;
- entry.canBubble = true;
- return entry;
+ n.flags |= FLAG_BUBBLE;
+
+ return new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(IMPORTANCE_HIGH)
+ .setCanBubble(true)
+ .build();
}
private NotificationEntry createNotification(int importance) {
@@ -563,11 +577,14 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
.setContentTitle("title")
.setContentText("content text")
.build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.importance = importance;
- return entry;
+
+ return new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(importance)
+ .build();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 55ce8d60f2b4..86869bd65900 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -38,7 +38,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
import org.junit.Test;
@@ -91,7 +90,11 @@ public class NotificationListenerTest extends SysuiTestCase {
@Test
public void testNotificationUpdateCallsUpdateNotification() {
- when(mNotificationData.get(mSbn.getKey())).thenReturn(NotificationEntry.buildForTest(mSbn));
+ when(mNotificationData.get(mSbn.getKey()))
+ .thenReturn(
+ new NotificationEntryBuilder()
+ .setSbn(mSbn)
+ .build());
mListener.onNotificationPosted(mSbn, mRanking);
TestableLooper.get(this).processAllMessages();
verify(mEntryManager).updateNotification(mSbn, mRanking);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 57dd8c94c790..a02764320b6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -19,11 +19,12 @@ package com.android.systemui.statusbar;
import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -166,7 +167,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
when(mNotificationData.isHighPriority(any())).thenReturn(false);
- assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
+ NotificationEntry entry = new NotificationEntryBuilder().build();
+ entry.setBucket(BUCKET_SILENT);
+
+ assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
}
@Test
@@ -179,7 +183,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
when(mNotificationData.isHighPriority(any())).thenReturn(false);
- assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
+ NotificationEntry entry = new NotificationEntryBuilder().build();
+ entry.setBucket(BUCKET_SILENT);
+ assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
}
private class TestNotificationLockscreenUserManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 99d09f18eca6..852ddb2ae710 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -63,7 +63,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
private TestableNotificationRemoteInputManager mRemoteInputManager;
- private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
@@ -78,9 +77,13 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
() -> mock(ShadeController.class),
mStateController,
Handler.createAsync(Looper.myLooper()));
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
- 0, new Notification(), UserHandle.CURRENT, null, 0);
- mEntry = NotificationEntry.buildForTest(mSbn);
+ mEntry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setNotification(new Notification())
+ .setUser(UserHandle.CURRENT)
+ .build();
mEntry.setRow(mRow);
mRemoteInputManager.setUpWithPresenterForTest(mCallback,
@@ -94,7 +97,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Test
public void testPerformOnRemoveNotification() {
when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
- mRemoteInputManager.onPerformRemoveNotification(mEntry, mSbn.getKey());
+ mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.key());
verify(mController).removeRemoteInput(mEntry, null);
}
@@ -175,7 +178,9 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
// Setup a notification entry with 1 remote input.
StatusBarNotification newSbn =
mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
- NotificationEntry entry = NotificationEntry.buildForTest(newSbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(newSbn)
+ .build();
// Try rebuilding to add another reply.
newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 7cd58193a3e7..de77af8f4d14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -20,6 +20,8 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+
import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
@@ -187,7 +189,9 @@ public class NotificationTestHelper {
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, pkg, UID, USER_HANDLE,
0 /* extraInflationFlags */, IMPORTANCE_HIGH);
- row.getEntry().canBubble = true;
+ modifyRanking(row.getEntry())
+ .setCanBubble(true)
+ .build();
return row;
}
@@ -295,17 +299,7 @@ public class NotificationTestHelper {
row.setGroupManager(mGroupManager);
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(aboveShelf -> {});
- StatusBarNotification sbn = new StatusBarNotification(
- pkg,
- pkg,
- mId++,
- null /* tag */,
- uid,
- 2000 /* initialPid */,
- notification,
- userHandle,
- null /* overrideGroupKey */,
- System.currentTimeMillis());
+
final NotificationChannel channel =
new NotificationChannel(
notification.getChannelId(),
@@ -313,14 +307,20 @@ public class NotificationTestHelper {
importance);
channel.setBlockableSystem(true);
- NotificationEntry entry = new NotificationEntry(
- sbn,
- new RankingBuilder()
- .setKey(sbn.getKey())
- .setChannel(channel)
- .build());
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(pkg)
+ .setOpPkg(pkg)
+ .setId(mId++)
+ .setUid(uid)
+ .setInitialPid(2000)
+ .setNotification(notification)
+ .setUser(userHandle)
+ .setPostTime(System.currentTimeMillis())
+ .setChannel(channel)
+ .build();
+
entry.setRow(row);
- entry.createIcons(mContext, sbn);
+ entry.createIcons(mContext, entry.sbn());
row.setEntry(entry);
row.getNotificationInflater().addInflationFlags(extraInflationFlags);
NotificationContentInflaterTest.runThenWaitForInflation(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index f63389c911c9..d00be568cbff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -30,6 +30,7 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Test;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -199,11 +200,11 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
private NotificationUiAdjustment createUiAdjustmentFromSmartActions(
String key, List<Notification.Action> actions) {
- return new NotificationUiAdjustment(key, actions, new CharSequence[0]);
+ return new NotificationUiAdjustment(key, actions, null);
}
private NotificationUiAdjustment createUiAdjustmentFromSmartReplies(
String key, CharSequence[] replies) {
- return new NotificationUiAdjustment(key, Collections.emptyList(), replies);
+ return new NotificationUiAdjustment(key, null, Arrays.asList(replies));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 388cf582237b..9e7250412499 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -120,7 +120,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
private NotificationEntry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
- NotificationEntry entry = NotificationEntry.buildForTest(row.getStatusBarNotification());
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(row.getStatusBarNotification())
+ .build();
entry.setRow(row);
return entry;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index bbdc4b7fc360..05f179ed4620 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationManager.Importance;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Standard builder class for Ranking objects. For use in tests that need to craft the underlying
@@ -33,27 +37,143 @@ public class RankingBuilder {
private boolean mMatchesInterruptionFilter = false;
private int mVisibilityOverride = 0;
private int mSuppressedVisualEffects = 0;
- private int mImportance = 0;
+ @Importance private int mImportance = 0;
private CharSequence mExplanation = "test_explanation";
private String mOverrideGroupKey = null;
private NotificationChannel mChannel = null;
- private ArrayList<String> mOverridePeople = null;
+ private ArrayList<String> mAdditionalPeople = null;
private ArrayList<SnoozeCriterion> mSnoozeCriteria = null;
- private boolean mShowBadge = false;
+ private boolean mCanShowBadge = false;
private int mUserSentiment = 0;
- private boolean mHidden = false;
+ private boolean mIsSuspended = false;
private long mLastAudiblyAlertedMs = 0;
private boolean mNoisy = false;
- private ArrayList<Notification.Action> mSmartActions = null;
- private ArrayList<CharSequence> mSmartReplies = null;
+ private ArrayList<Notification.Action> mSmartActions = new ArrayList<>();
+ private ArrayList<CharSequence> mSmartReplies = new ArrayList<>();
private boolean mCanBubble = false;
+ public RankingBuilder() {
+ }
+
+ public RankingBuilder(Ranking ranking) {
+ mKey = ranking.getKey();
+ mRank = ranking.getRank();
+ mMatchesInterruptionFilter = ranking.matchesInterruptionFilter();
+ mVisibilityOverride = ranking.getVisibilityOverride();
+ mSuppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ mImportance = ranking.getImportance();
+ mExplanation = ranking.getImportanceExplanation();
+ mOverrideGroupKey = ranking.getOverrideGroupKey();
+ mChannel = ranking.getChannel();
+ mAdditionalPeople = copyList(ranking.getAdditionalPeople());
+ mSnoozeCriteria = copyList(ranking.getSnoozeCriteria());
+ mCanShowBadge = ranking.canShowBadge();
+ mUserSentiment = ranking.getUserSentiment();
+ mIsSuspended = ranking.isSuspended();
+ mLastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
+ mNoisy = ranking.isNoisy();
+ mSmartActions = copyList(ranking.getSmartActions());
+ mSmartReplies = copyList(ranking.getSmartReplies());
+ mCanBubble = ranking.canBubble();
+ }
+
+ public Ranking build() {
+ final Ranking ranking = new Ranking();
+ ranking.populate(
+ mKey,
+ mRank,
+ mMatchesInterruptionFilter,
+ mVisibilityOverride,
+ mSuppressedVisualEffects,
+ mImportance,
+ mExplanation,
+ mOverrideGroupKey,
+ mChannel,
+ mAdditionalPeople,
+ mSnoozeCriteria,
+ mCanShowBadge,
+ mUserSentiment,
+ mIsSuspended,
+ mLastAudiblyAlertedMs,
+ mNoisy,
+ mSmartActions,
+ mSmartReplies,
+ mCanBubble);
+ return ranking;
+ }
+
public RankingBuilder setKey(String key) {
mKey = key;
return this;
}
- public RankingBuilder setImportance(int importance) {
+ public RankingBuilder setRank(int rank) {
+ mRank = rank;
+ return this;
+ }
+
+ public RankingBuilder setMatchesInterruptionFilter(boolean matchesInterruptionFilter) {
+ mMatchesInterruptionFilter = matchesInterruptionFilter;
+ return this;
+ }
+
+ public RankingBuilder setVisibilityOverride(int visibilityOverride) {
+ mVisibilityOverride = visibilityOverride;
+ return this;
+ }
+
+ public RankingBuilder setSuppressedVisualEffects(int suppressedVisualEffects) {
+ mSuppressedVisualEffects = suppressedVisualEffects;
+ return this;
+ }
+
+ public RankingBuilder setExplanation(CharSequence explanation) {
+ mExplanation = explanation;
+ return this;
+ }
+
+ public RankingBuilder setOverrideGroupKey(String overrideGroupKey) {
+ mOverrideGroupKey = overrideGroupKey;
+ return this;
+ }
+
+ public RankingBuilder setAdditionalPeople(ArrayList<String> additionalPeople) {
+ mAdditionalPeople = additionalPeople;
+ return this;
+ }
+
+ public RankingBuilder setSnoozeCriteria(
+ ArrayList<SnoozeCriterion> snoozeCriteria) {
+ mSnoozeCriteria = snoozeCriteria;
+ return this;
+ }
+
+ public RankingBuilder setCanShowBadge(boolean canShowBadge) {
+ mCanShowBadge = canShowBadge;
+ return this;
+ }
+
+ public RankingBuilder setSuspended(boolean suspended) {
+ mIsSuspended = suspended;
+ return this;
+ }
+
+ public RankingBuilder setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
+ mLastAudiblyAlertedMs = lastAudiblyAlertedMs;
+ return this;
+ }
+
+ public RankingBuilder setNoisy(boolean noisy) {
+ mNoisy = noisy;
+ return this;
+ }
+
+ public RankingBuilder setCanBubble(boolean canBubble) {
+ mCanBubble = canBubble;
+ return this;
+ }
+
+ public RankingBuilder setImportance(@Importance int importance) {
mImportance = importance;
return this;
}
@@ -68,38 +188,31 @@ public class RankingBuilder {
return this;
}
- public RankingBuilder setSmartActions(ArrayList<Notification.Action> smartActions) {
+ public RankingBuilder setSmartActions(@NonNull ArrayList<Notification.Action> smartActions) {
mSmartActions = smartActions;
return this;
}
- public RankingBuilder setSmartReplies(ArrayList<CharSequence> smartReplies) {
+ public RankingBuilder setSmartActions(Notification.Action... smartActions) {
+ mSmartActions = new ArrayList<>(Arrays.asList(smartActions));
+ return this;
+ }
+
+ public RankingBuilder setSmartReplies(@NonNull ArrayList<CharSequence> smartReplies) {
mSmartReplies = smartReplies;
return this;
}
- public Ranking build() {
- final Ranking ranking = new Ranking();
- ranking.populate(
- mKey,
- mRank,
- mMatchesInterruptionFilter,
- mVisibilityOverride,
- mSuppressedVisualEffects,
- mImportance,
- mExplanation,
- mOverrideGroupKey,
- mChannel,
- mOverridePeople,
- mSnoozeCriteria,
- mShowBadge,
- mUserSentiment,
- mHidden,
- mLastAudiblyAlertedMs,
- mNoisy,
- mSmartActions,
- mSmartReplies,
- mCanBubble);
- return ranking;
+ public RankingBuilder setSmartReplies(CharSequence... smartReplies) {
+ mSmartReplies = new ArrayList<>(Arrays.asList(smartReplies));
+ return this;
+ }
+
+ private static <E> ArrayList<E> copyList(List<E> list) {
+ if (list == null) {
+ return null;
+ } else {
+ return new ArrayList<>(list);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
new file mode 100644
index 000000000000..9bc962c77019
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -0,0 +1,119 @@
+/*
+ * 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.statusbar;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Convenience builder for {@link StatusBarNotification} since its constructor is terrifying.
+ *
+ * Only for use in tests.
+ */
+public class SbnBuilder {
+ private String mPkg = "test_pkg";
+ private String mOpPkg;
+ private int mId;
+ private String mTag;
+ private int mUid;
+ private int mInitialPid;
+ private Notification mNotification = new Notification();
+ private UserHandle mUser = UserHandle.of(0);
+ private String mOverrideGroupKey;
+ private long mPostTime;
+
+ public SbnBuilder() {
+ }
+
+ public SbnBuilder(StatusBarNotification source) {
+ mPkg = source.getPackageName();
+ mOpPkg = source.getOpPkg();
+ mId = source.getId();
+ mTag = source.getTag();
+ mUid = source.getUid();
+ mInitialPid = source.getInitialPid();
+ mNotification = source.getNotification();
+ mUser = source.getUser();
+ mOverrideGroupKey = source.getOverrideGroupKey();
+ mPostTime = source.getPostTime();
+ }
+
+ public StatusBarNotification build() {
+ return new StatusBarNotification(
+ mPkg,
+ mOpPkg,
+ mId,
+ mTag,
+ mUid,
+ mInitialPid,
+ mNotification,
+ mUser,
+ mOverrideGroupKey,
+ mPostTime);
+ }
+
+ public SbnBuilder setPkg(String pkg) {
+ mPkg = pkg;
+ return this;
+ }
+
+ public SbnBuilder setOpPkg(String opPkg) {
+ mOpPkg = opPkg;
+ return this;
+ }
+
+ public SbnBuilder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ public SbnBuilder setTag(String tag) {
+ mTag = tag;
+ return this;
+ }
+
+ public SbnBuilder setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
+ public SbnBuilder setInitialPid(int initialPid) {
+ mInitialPid = initialPid;
+ return this;
+ }
+
+ public SbnBuilder setNotification(Notification notification) {
+ mNotification = notification;
+ return this;
+ }
+
+ public SbnBuilder setUser(UserHandle user) {
+ mUser = user;
+ return this;
+ }
+
+ public SbnBuilder setOverrideGroupKey(String overrideGroupKey) {
+ mOverrideGroupKey = overrideGroupKey;
+ return this;
+ }
+
+ public SbnBuilder setPostTime(long postTime) {
+ mPostTime = postTime;
+ return this;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index e54ea6a7c8f9..88fb3e1d5c19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -97,7 +97,9 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
- mEntry = NotificationEntry.buildForTest(mSbn);
+ mEntry = new NotificationEntryBuilder()
+ .setSbn(mSbn)
+ .build();
}
@Test
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 36b143bdace7..53d6bffdc896 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
@@ -65,6 +65,7 @@ import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -237,9 +238,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
- 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
- mEntry = NotificationEntry.buildForTest(mSbn);
+
+ mEntry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setNotification(n.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ mSbn = mEntry.sbn();
+
mEntry.expandedIcon = mock(StatusBarIconView.class);
mEntryManager = new TestableNotificationEntryManager(mContext);
@@ -292,7 +300,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
assertNotNull(entry.getRow());
- assertEquals(mEntry.userSentiment,
+ assertEquals(mEntry.getUserSentiment(),
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@@ -317,7 +325,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
verify(mEntryListener).onPostEntryUpdated(mEntry);
assertNotNull(mEntry.getRow());
- assertEquals(mEntry.userSentiment,
+ assertEquals(mEntry.getUserSentiment(),
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@@ -403,8 +411,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow).setEntry(eq(mEntry));
- assertEquals(1, mEntry.systemGeneratedSmartActions.size());
- assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
+ assertEquals(1, mEntry.getSmartActions().size());
+ assertEquals("action", mEntry.getSmartActions().get(0).title);
verify(mEntryListener).onNotificationRankingUpdated(mRankingMap);
}
@@ -419,7 +427,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(0, mEntry.systemGeneratedSmartActions.size());
+ assertNull(mEntry.getSmartActions());
}
@Test
@@ -433,8 +441,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(1, mEntry.systemGeneratedSmartActions.size());
- assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
+ assertEquals(1, mEntry.getSmartActions().size());
+ assertEquals("action", mEntry.getSmartActions().get(0).title);
}
@Test
@@ -448,8 +456,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
- assertEquals(1, mEntry.systemGeneratedSmartActions.size());
- assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
+ assertEquals(1, mEntry.getSmartActions().size());
+ assertEquals("action", mEntry.getSmartActions().get(0).title);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index b5223161dae4..edd0a10672fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -195,13 +196,16 @@ public class NotificationFilterTest extends SysuiTestCase {
// test should filter out hidden notifications:
// hidden
- NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
- entry.suspended = true;
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSuspended(true)
+ .build();
+
assertTrue(mNotificationFilter.shouldFilterOut(entry));
// not hidden
- entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
- entry.suspended = false;
+ entry = new NotificationEntryBuilder()
+ .setSuspended(false)
+ .build();
assertFalse(mNotificationFilter.shouldFilterOut(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index e42d1558afa9..8d496a72e3b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -30,7 +30,6 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Notification;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
@@ -41,6 +40,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -73,7 +73,7 @@ public class NotificationListControllerTest extends SysuiTestCase {
private DeviceProvisionedListener mProvisionedListener;
// TODO: Remove this once EntryManager no longer needs to be mocked
- private NotificationData mNotificationData = new NotificationData();
+ private NotificationData mNotificationData = new NotificationData(mContext);
private int mNextNotifId = 0;
@@ -222,19 +222,14 @@ public class NotificationListControllerTest extends SysuiTestCase {
.setContentTitle("Title")
.setContentText("Text");
- StatusBarNotification notification =
- new StatusBarNotification(
- TEST_PACKAGE_NAME,
- TEST_PACKAGE_NAME,
- mNextNotifId,
- null,
- TEST_UID,
- 0,
- n.build(),
- new UserHandle(ActivityManager.getCurrentUser()),
- null,
- 0);
- return NotificationEntry.buildForTest(notification);
+ return new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setId(mNextNotifId)
+ .setUid(TEST_UID)
+ .setNotification(n.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
}
private static final String TEST_PACKAGE_NAME = "test";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 49229ca050c3..6a4ddc7ec202 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -25,13 +25,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -62,7 +62,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase {
mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
- mEntry = NotificationEntry.buildForTest(mock(StatusBarNotification.class));
+ mEntry = new NotificationEntryBuilder().build();
mEntry.setRow(mRow);
when(mRow.getEntry()).thenReturn(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 45173a2ef797..ab7f960d6a15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -27,10 +27,13 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_CHANNEL;
import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_IMPORTANCE;
import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_RANK;
import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_VIS_EFFECTS;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import static junit.framework.Assert.assertEquals;
@@ -47,6 +50,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -70,7 +74,10 @@ import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -83,9 +90,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
@SmallTest
@@ -98,8 +103,8 @@ public class NotificationDataTest extends SysuiTestCase {
private static final NotificationChannel NOTIFICATION_CHANNEL =
new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
- private final StatusBarNotification mMockStatusBarNotification =
- mock(StatusBarNotification.class);
+ private NotificationEntry mEntry;
+
@Mock
ForegroundServiceController mFsc;
@Mock
@@ -113,9 +118,10 @@ public class NotificationDataTest extends SysuiTestCase {
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
- when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
- when(mMockStatusBarNotification.getKey()).thenReturn("mock_key");
+
+ mEntry = new NotificationEntryBuilder()
+ .setUid(UID_NORMAL)
+ .build();
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -133,7 +139,7 @@ public class NotificationDataTest extends SysuiTestCase {
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mNotificationData = new TestableNotificationData();
+ mNotificationData = new TestableNotificationData(mContext);
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
Dependency.get(InitController.class).executePostInitTasks();
@@ -231,57 +237,57 @@ public class NotificationDataTest extends SysuiTestCase {
public void testIsExemptFromDndVisualSuppression_foreground() {
initStatusBarNotification(false);
- Notification n = mMockStatusBarNotification.getNotification();
- n.flags = Notification.FLAG_FOREGROUND_SERVICE;
- NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
- entry.setRow(mRow);
- mNotificationData.add(entry);
+ mEntry.sbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+ mEntry.setRow(mRow);
+ mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(entry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.key, override);
- assertTrue(entry.isExemptFromDndVisualSuppression());
- assertFalse(entry.shouldSuppressAmbient());
+ assertTrue(mEntry.isExemptFromDndVisualSuppression());
+ assertFalse(mEntry.shouldSuppressAmbient());
}
@Test
public void testIsExemptFromDndVisualSuppression_media() {
initStatusBarNotification(false);
- Notification n = mMockStatusBarNotification.getNotification();
+ Notification n = mEntry.sbn().getNotification();
Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
n = nb.build();
- when(mMockStatusBarNotification.getNotification()).thenReturn(n);
- NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
- entry.setRow(mRow);
- mNotificationData.add(entry);
+ modifySbn(mEntry)
+ .setNotification(n)
+ .build();
+ mEntry.setRow(mRow);
+ mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(entry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.key, override);
- assertTrue(entry.isExemptFromDndVisualSuppression());
- assertFalse(entry.shouldSuppressAmbient());
+ assertTrue(mEntry.isExemptFromDndVisualSuppression());
+ assertFalse(mEntry.shouldSuppressAmbient());
}
@Test
public void testIsExemptFromDndVisualSuppression_system() {
initStatusBarNotification(false);
- NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
- entry.setRow(mRow);
- entry.mIsSystemNotification = true;
- mNotificationData.add(entry);
+ mEntry.setRow(mRow);
+ mEntry.mIsSystemNotification = true;
+ mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(entry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.key, override);
- assertTrue(entry.isExemptFromDndVisualSuppression());
- assertFalse(entry.shouldSuppressAmbient());
+ assertTrue(mEntry.isExemptFromDndVisualSuppression());
+ assertFalse(mEntry.shouldSuppressAmbient());
}
@Test
public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
initStatusBarNotification(false);
- NotificationEntry entry = NotificationEntry.buildForTest(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setUid(UID_NORMAL)
+ .build();
entry.setRow(mRow);
entry.mIsSystemNotification = true;
Bundle override = new Bundle();
@@ -289,63 +295,70 @@ public class NotificationDataTest extends SysuiTestCase {
mNotificationData.rankingOverrides.put(entry.key, override);
mNotificationData.add(entry);
- when(mMockStatusBarNotification.getNotification()).thenReturn(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
-
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build())
+ .build();
assertFalse(entry.isExemptFromDndVisualSuppression());
assertTrue(entry.shouldSuppressAmbient());
- when(mMockStatusBarNotification.getNotification()).thenReturn(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
-
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "")
+ .setCategory(CATEGORY_REMINDER)
+ .build())
+ .build();
assertFalse(entry.isExemptFromDndVisualSuppression());
- when(mMockStatusBarNotification.getNotification()).thenReturn(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
-
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build())
+ .build();
assertFalse(entry.isExemptFromDndVisualSuppression());
- when(mMockStatusBarNotification.getNotification()).thenReturn(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
-
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build())
+ .build();
assertFalse(entry.isExemptFromDndVisualSuppression());
- when(mMockStatusBarNotification.getNotification()).thenReturn(
- new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
-
+ modifySbn(entry)
+ .setNotification(
+ new Notification.Builder(mContext, "")
+ .setCategory(CATEGORY_MESSAGE)
+ .build())
+ .build();
assertFalse(entry.isExemptFromDndVisualSuppression());
}
@Test
public void testCreateNotificationDataEntry_RankingUpdate() {
- Ranking ranking = mock(Ranking.class);
- initStatusBarNotification(false);
+ StatusBarNotification sbn = new SbnBuilder().build();
+ sbn.getNotification().actions =
+ new Notification.Action[] { createContextualAction("appGeneratedAction") };
- List<Notification.Action> appGeneratedSmartActions =
- Collections.singletonList(createContextualAction("appGeneratedAction"));
- mMockStatusBarNotification.getNotification().actions =
- appGeneratedSmartActions.toArray(new Notification.Action[0]);
-
- List<Notification.Action> systemGeneratedSmartActions =
- Collections.singletonList(createAction("systemGeneratedAction"));
- when(ranking.getSmartActions()).thenReturn(systemGeneratedSmartActions);
-
- when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL);
-
- when(ranking.getUserSentiment()).thenReturn(Ranking.USER_SENTIMENT_NEGATIVE);
+ ArrayList<Notification.Action> systemGeneratedSmartActions =
+ createActions("systemGeneratedAction");
SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
snoozeCriterions.add(snoozeCriterion);
- when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
+
+ Ranking ranking = new RankingBuilder()
+ .setKey(sbn.getKey())
+ .setSmartActions(systemGeneratedSmartActions)
+ .setChannel(NOTIFICATION_CHANNEL)
+ .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
+ .setSnoozeCriteria(snoozeCriterions)
+ .build();
NotificationEntry entry =
- new NotificationEntry(mMockStatusBarNotification, ranking);
+ new NotificationEntry(sbn, ranking);
- assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
+ assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
- assertEquals(snoozeCriterions, entry.snoozeCriteria);
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment());
+ assertEquals(snoozeCriterions, entry.getSnoozeCriteria());
}
@Test
@@ -366,10 +379,15 @@ public class NotificationDataTest extends SysuiTestCase {
Notification notification = new Notification.Builder(mContext, "test")
.addExtras(bundle)
.build();
- StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notification, mContext.getUser(), "", 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notification)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
entry.setHasSentReply();
assertTrue(entry.isLastMessageFromReply());
@@ -472,9 +490,14 @@ public class NotificationDataTest extends SysuiTestCase {
Notification aN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
.build();
- StatusBarNotification aSbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- aN, mContext.getUser(), "", 0);
- NotificationEntry a = NotificationEntry.buildForTest(aSbn);
+ NotificationEntry a = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
a.setRow(mock(ExpandableNotificationRow.class));
a.setIsHighPriority(false);
@@ -486,9 +509,14 @@ public class NotificationDataTest extends SysuiTestCase {
Notification bN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
.build();
- StatusBarNotification bSbn = new StatusBarNotification("pkg2", "pkg2", 0, "tag", 0, 0,
- bN, mContext.getUser(), "", 0);
- NotificationEntry b = NotificationEntry.buildForTest(bSbn);
+ NotificationEntry b = new NotificationEntryBuilder()
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
b.setIsHighPriority(true);
b.setRow(mock(ExpandableNotificationRow.class));
@@ -502,14 +530,18 @@ public class NotificationDataTest extends SysuiTestCase {
@Test
public void testSort_samePriorityUsesNMSRank() {
- // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
- // front
+ // NMS rank says A and then B, and they are the same priority so use that rank
Notification aN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
.build();
- StatusBarNotification aSbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- aN, mContext.getUser(), "", 0);
- NotificationEntry a = NotificationEntry.buildForTest(aSbn);
+ NotificationEntry a = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
a.setRow(mock(ExpandableNotificationRow.class));
a.setIsHighPriority(false);
@@ -521,9 +553,14 @@ public class NotificationDataTest extends SysuiTestCase {
Notification bN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
.build();
- StatusBarNotification bSbn = new StatusBarNotification("pkg2", "pkg2", 0, "tag", 0, 0,
- bN, mContext.getUser(), "", 0);
- NotificationEntry b = NotificationEntry.buildForTest(bSbn);
+ NotificationEntry b = new NotificationEntryBuilder()
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
b.setRow(mock(ExpandableNotificationRow.class));
b.setIsHighPriority(false);
@@ -536,59 +573,50 @@ public class NotificationDataTest extends SysuiTestCase {
}
@Test
- public void testSort_properlySetsIsTopBucket() {
-
+ public void testSort_properlySetsAlertingBucket() {
Notification notification = new Notification.Builder(mContext, "test")
.build();
- StatusBarNotification sbn = new StatusBarNotification(
- "pkg",
- "pkg",
- 0,
- "tag",
- 0,
- 0,
- notification,
- mContext.getUser(),
- "",
- 0);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notification)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT);
- mNotificationData.rankingOverrides.put(sbn.getKey(), override);
+ mNotificationData.rankingOverrides.put(entry.key(), override);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
entry.setRow(mRow);
mNotificationData.add(entry);
- assertTrue(entry.isTopBucket());
+ assertEquals(entry.getBucket(), BUCKET_ALERTING);
}
@Test
- public void testSort_properlySetsIsNotTopBucket() {
+ public void testSort_properlySetsSilentBucket() {
Notification notification = new Notification.Builder(mContext, "test")
.build();
- StatusBarNotification sbn = new StatusBarNotification(
- "pkg",
- "pkg",
- 0,
- "tag",
- 0,
- 0,
- notification,
- mContext.getUser(),
- "",
- 0);
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(notification)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build();
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- mNotificationData.rankingOverrides.put(sbn.getKey(), override);
+ mNotificationData.rankingOverrides.put(entry.key(), override);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
entry.setRow(mRow);
-
mNotificationData.add(entry);
- assertFalse(entry.isTopBucket());
+ assertEquals(entry.getBucket(), BUCKET_SILENT);
}
private void initStatusBarNotification(boolean allowDuringSetup) {
@@ -597,12 +625,14 @@ public class NotificationDataTest extends SysuiTestCase {
Notification notification = new Notification.Builder(mContext, "test")
.addExtras(bundle)
.build();
- when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+ modifySbn(mEntry)
+ .setNotification(notification)
+ .build();
}
public static class TestableNotificationData extends NotificationData {
- public TestableNotificationData() {
- super();
+ public TestableNotificationData(Context context) {
+ super(context);
}
public static final String OVERRIDE_RANK = "r";
@@ -684,6 +714,11 @@ public class NotificationDataTest extends SysuiTestCase {
? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
: currentReplies,
overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()));
+ } else {
+ outRanking.populate(
+ new RankingBuilder()
+ .setKey(key)
+ .build());
}
return true;
}
@@ -704,4 +739,12 @@ public class NotificationDataTest extends SysuiTestCase {
title,
PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
}
+
+ private ArrayList<Notification.Action> createActions(String... titles) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (String title : titles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index ff3a2e224788..24cd056b789c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -29,7 +29,6 @@ import android.app.Notification;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -40,6 +39,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -93,10 +93,13 @@ public class NotificationLoggerTest extends SysuiTestCase {
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, TEST_UID,
- 0, new Notification(), UserHandle.CURRENT, null, 0);
- mEntry = NotificationEntry.buildForTest(sbn);
+ mEntry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setNotification(new Notification())
+ .setUser(UserHandle.CURRENT)
+ .build();
mEntry.setRow(mRow);
mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index a14557bd9684..c56a168a29d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
@@ -51,7 +52,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
@@ -328,11 +328,11 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
// Give each child a unique channel id/name.
int i = 0;
for (ExpandableNotificationRow childRow : childRows) {
- childRow.getEntry().setRanking(new RankingBuilder()
+ modifyRanking(childRow.getEntry())
.setChannel(
new NotificationChannel(
"id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
- .build());
+ .build();
i++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index a26cdbd30b47..6d6439532912 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -21,6 +21,8 @@ import static android.service.notification.NotificationListenerService.Ranking.U
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -44,7 +46,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.util.Assert;
@@ -130,7 +131,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Test
public void testPerhapsShowBlockingHelper_shown() throws Exception {
ExpandableNotificationRow row = createBlockableRowSpy();
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
@@ -142,14 +145,16 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
int i = 0;
for (ExpandableNotificationRow childRow : groupRow.getNotificationChildren()) {
- childRow.getEntry().setRanking(new RankingBuilder()
+ modifyRanking(childRow.getEntry())
.setChannel(
new NotificationChannel(
Integer.toString(i++), "", IMPORTANCE_DEFAULT))
- .build());
+ .build();
}
- groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(groupRow.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
@@ -159,7 +164,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Test
public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
- groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(groupRow.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
@@ -174,7 +181,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
// as other factors such as view expansion may cause us to get the parent row back instead
// of the child row.
ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
- childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(childRow.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertFalse(childRow.getIsNonblockable());
assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
@@ -185,7 +194,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Test
public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
ExpandableNotificationRow row = createBlockableRowSpy();
- row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEUTRAL)
+ .build();
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
}
@@ -194,7 +205,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
throws Exception {
ExpandableNotificationRow row = createBlockableRowSpy();
- row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_POSITIVE)
+ .build();
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
}
@@ -202,7 +215,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Test
public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
ExpandableNotificationRow row = createBlockableRowSpy();
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
// Hide the shade
mBlockingHelperManager.setNotificationShadeExpanded(0f);
@@ -213,7 +228,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception {
ExpandableNotificationRow row = createBlockableRowSpy();
when(row.getIsNonblockable()).thenReturn(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
}
@@ -226,7 +243,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
// as other factors such as view expansion may cause us to get the parent row back instead
// of the child row.
ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
- childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(childRow.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
}
@@ -234,7 +253,9 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
@Test
public void testBlockingHelperShowAndDismiss() throws Exception{
ExpandableNotificationRow row = createBlockableRowSpy();
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
when(row.isAttachedToWindow()).thenReturn(true);
// Show check
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 09c4179185fc..db6b6130ebf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -22,6 +22,8 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -62,7 +64,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -313,7 +314,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -342,7 +345,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(false);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -371,8 +376,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
- row.getEntry().importance = IMPORTANCE_DEFAULT;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .setImportance(IMPORTANCE_DEFAULT)
+ .build();
row.getEntry().setIsHighPriority(true);
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -402,7 +409,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(false);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
@@ -432,7 +441,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationInfo notificationInfoView = mock(NotificationInfo.class);
ExpandableNotificationRow row = spy(mHelper.createRow());
row.setBlockingHelperShowing(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+ modifyRanking(row.getEntry())
+ .setUserSentiment(USER_SENTIMENT_NEGATIVE)
+ .build();
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
@@ -499,10 +510,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
try {
ExpandableNotificationRow row = mHelper.createRow(nb.build());
- row.getEntry().setRanking(
- new RankingBuilder()
- .setChannel(mTestNotificationChannel)
- .build());
+ modifyRanking(row.getEntry())
+ .setChannel(mTestNotificationChannel)
+ .build();
return row;
} catch (Exception e) {
fail();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 096acf9d9ce4..9eba4ebeb994 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -28,9 +28,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.NotificationChannel;
import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -40,7 +38,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -61,11 +59,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mRow = mock(ExpandableNotificationRow.class);
- NotificationEntry entry = new NotificationEntry(
- mock(StatusBarNotification.class),
- new RankingBuilder()
- .setChannel(mock(NotificationChannel.class))
- .build());
+ NotificationEntry entry = new NotificationEntryBuilder().build();
when(mRow.getEntry()).thenReturn(entry);
}
@@ -75,7 +69,6 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
}
-
@Test
public void testAttachDetach() {
NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 524ad85c3a98..addceb5def6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -64,7 +64,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRoundnessManager = new NotificationRoundnessManager(mBypassController);
+ mRoundnessManager = new NotificationRoundnessManager(mBypassController, mContext);
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
mFirst = testHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 59d0f912d38e..56ed0e3a6af3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -18,6 +18,9 @@ package com.android.systemui.statusbar.notification.stack;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -74,7 +77,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mActivityStarterDelegate,
mStatusBarStateController,
mConfigurationController,
- true);
+ 2);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
@@ -263,8 +266,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
when(notifRow.getEntry().isHighPriority())
.thenReturn(children[i] == ChildType.HIPRI);
- when(notifRow.getEntry().isTopBucket())
- .thenReturn(children[i] == ChildType.HIPRI);
+ when(notifRow.getEntry().getBucket()).thenReturn(
+ children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT);
when(notifRow.getParent()).thenReturn(mNssl);
child = notifRow;
break;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index de3623f86c44..ef9665a80848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -121,7 +122,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
@Test
public void testCanRemoveImmediately_notTopEntry() {
- NotificationEntry laterEntry = NotificationEntry.buildForTest(createNewNotification(1));
+ NotificationEntry laterEntry = new NotificationEntryBuilder()
+ .setSbn(createNewNotification(1))
+ .build();
laterEntry.setRow(mRow);
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index e33545bba7a2..f1a7905d1d42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -24,9 +24,9 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -76,22 +76,18 @@ public final class NotificationGroupTestHelper {
.setGroupSummary(isSummary)
.setGroup(TEST_GROUP_ID)
.build();
- StatusBarNotification sbn = new StatusBarNotification(
- TEST_PACKAGE_NAME /* pkg */,
- TEST_PACKAGE_NAME,
- id,
- null /* tag */,
- 0, /* uid */
- 0 /* initialPid */,
- notif,
- new UserHandle(ActivityManager.getCurrentUser()),
- null /* overrideGroupKey */,
- 0 /* postTime */);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setId(id)
+ .setNotification(notif)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
- when(row.getStatusBarNotification()).thenReturn(sbn);
+ when(row.getStatusBarNotification()).thenReturn(entry.sbn());
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
return entry;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 2623b46ba50f..5d3cdc88aa99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -243,7 +243,7 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void transitionToPulsing() {
+ public void transitionToPulsing_withFrontAlphaUpdates() {
// Pre-condition
// Need to go to AoD first because PULSING doesn't change
// the back scrim opacity - otherwise it would hide AoD wallpapers.
@@ -267,11 +267,22 @@ public class ScrimControllerTest extends SysuiTestCase {
true /* behind */,
false /* bubble */);
+ // ... and when ambient goes dark, front scrim should be semi-transparent
+ mScrimController.setAodFrontScrimAlpha(0.5f);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be semi-transparent
+ assertScrimAlpha(SEMI_TRANSPARENT /* front */,
+ OPAQUE /* back */,
+ TRANSPARENT /* bubble */);
+
mScrimController.setWakeLockScreenSensorActive(true);
mScrimController.finishAnimationsImmediately();
- assertScrimAlpha(TRANSPARENT /* front */,
+ assertScrimAlpha(SEMI_TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
+
+ // Reset value since enums are static.
+ mScrimController.setAodFrontScrimAlpha(0f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 45cd1e17ff93..24ec1097ea8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -24,8 +24,6 @@ import android.app.Notification;
import android.app.StatusBarManager;
import android.content.Context;
import android.metrics.LogMaker;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.support.test.metricshelper.MetricsAsserts;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -39,6 +37,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
@@ -84,29 +83,35 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
@Test
public void testHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
false /* animate */);
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while disabled",
- mStatusBar.canHeadsUp(entry, sbn));
+ mStatusBar.canHeadsUp(entry, entry.sbn()));
}
@Test
public void testHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
false /* animate */);
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
- mStatusBar.canHeadsUp(entry, sbn));
+ mStatusBar.canHeadsUp(entry, entry.sbn()));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index a1afd1d9de29..4eb9a3151116 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -51,9 +51,7 @@ import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.service.dreams.IDreamManager;
-import android.service.notification.StatusBarNotification;
import android.support.test.metricshelper.MetricsAsserts;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -88,6 +86,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -417,10 +416,14 @@ public class StatusBarTest extends SysuiTestCase {
.setGroupSummary(true)
.setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY)
.build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.importance = IMPORTANCE_HIGH;
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@@ -438,10 +441,14 @@ public class StatusBarTest extends SysuiTestCase {
.setGroupSummary(true)
.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
.build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.importance = IMPORTANCE_HIGH;
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@@ -455,11 +462,15 @@ public class StatusBarTest extends SysuiTestCase {
when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
- entry.importance = IMPORTANCE_HIGH;
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(IMPORTANCE_HIGH)
+ .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
+ .build();
assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
@@ -473,10 +484,14 @@ public class StatusBarTest extends SysuiTestCase {
when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
- UserHandle.of(0), null, 0);
- NotificationEntry entry = NotificationEntry.buildForTest(sbn);
- entry.importance = IMPORTANCE_HIGH;
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index bda0e39fb572..c1f376bee525 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -15,6 +15,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -29,7 +31,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
-import android.service.notification.StatusBarNotification;
import android.util.Pair;
import androidx.test.annotation.UiThreadTest;
@@ -41,6 +42,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationEntryHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions;
@@ -65,7 +68,6 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
new Intent("com.android.WHITELISTED_TEST_ACTION");
@Mock SmartReplyConstants mSmartReplyConstants;
- @Mock StatusBarNotification mStatusBarNotification;
@Mock Notification mNotification;
NotificationEntry mEntry;
@Mock RemoteInput mRemoteInput;
@@ -87,8 +89,9 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
mDependency.injectTestDependency(PackageManagerWrapper.class, mPackageManagerWrapper);
when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
- when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
- mEntry = NotificationEntry.buildForTest(mStatusBarNotification);
+ mEntry = new NotificationEntryBuilder()
+ .setNotification(mNotification)
+ .build();
when(mSmartReplyConstants.isEnabled()).thenReturn(true);
mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
@@ -99,7 +102,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() {
CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
List<Notification.Action> smartActions =
- createActions(new String[] {"Test Action 1", "Test Action 2"});
+ createActions("Test Action 1", "Test Action 2");
setupAppGeneratedSuggestions(smartReplies, smartActions);
when(mSmartReplyConstants.isEnabled()).thenReturn(false);
@@ -112,10 +115,11 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
@Test
public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() {
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
+
when(mSmartReplyConstants.isEnabled()).thenReturn(false);
SmartRepliesAndActions repliesAndActions =
@@ -133,7 +137,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
assertThat(repliesAndActions.smartActions).isNull();
}
@@ -142,13 +146,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
List<Notification.Action> smartActions =
- createActions(new String[] {"Test Action 1", "Test Action 2"});
+ createActions("Test Action 1", "Test Action 2");
setupAppGeneratedSuggestions(smartReplies, smartActions);
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
@@ -160,13 +164,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// replies.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .build();
+
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
- mEntry.systemGeneratedSmartReplies);
+ mEntry.getSmartReplies());
assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
assertThat(repliesAndActions.smartActions).isNull();
}
@@ -177,8 +183,9 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// replies.
setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ NotificationEntryHelper.modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -192,14 +199,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// actions.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ modifyRanking(mEntry)
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
assertThat(repliesAndActions.smartReplies).isNull();
assertThat(repliesAndActions.smartActions.actions)
- .isEqualTo(mEntry.systemGeneratedSmartActions);
+ .isEqualTo(mEntry.getSmartActions());
assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
}
@@ -209,18 +217,19 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
// replies.
List<Notification.Action> appGenSmartActions =
- createActions(new String[] {"Test Action 1", "Test Action 2"});
+ createActions("Test Action 1", "Test Action 2");
setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+ assertThat(repliesAndActions.smartReplies.choices)
+ .isEqualTo(Arrays.asList(appGenSmartReplies));
assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
@@ -232,10 +241,11 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// actions.
setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -253,16 +263,17 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
// suggestions.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
- mEntry.systemGeneratedSmartReplies);
+ mEntry.getSmartReplies());
// Since no apps are whitelisted no actions should be shown.
assertThat(repliesAndActions.smartActions.actions).isEmpty();
}
@@ -285,12 +296,14 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
// suggestions.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- List<Notification.Action> actions = new ArrayList<>();
+ ArrayList<Notification.Action> actions = new ArrayList<>();
actions.add(createAction("allowed action", WHITELISTED_TEST_INTENT));
actions.add(createAction("non-allowed action", TEST_INTENT));
- mEntry.systemGeneratedSmartActions = actions;
+
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(actions)
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -298,7 +311,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// Only the action for the whitelisted package should be allowed.
assertThat(repliesAndActions.smartActions.actions.size()).isEqualTo(1);
assertThat(repliesAndActions.smartActions.actions.get(0)).isEqualTo(
- mEntry.systemGeneratedSmartActions.get(0));
+ mEntry.getSmartActions().get(0));
}
@Test
@@ -310,25 +323,25 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
// Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
// suggestions.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.systemGeneratedSmartReplies =
- new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
- mEntry.systemGeneratedSmartActions =
- createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ modifyRanking(mEntry)
+ .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
+ .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
+ .build();
SmartRepliesAndActions repliesAndActions =
InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
// We don't restrict replies or actions in screen pinning mode.
assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
- mEntry.systemGeneratedSmartReplies);
+ mEntry.getSmartReplies());
assertThat(repliesAndActions.smartActions.actions).isEqualTo(
- mEntry.systemGeneratedSmartActions);
+ mEntry.getSmartActions());
}
@Test
public void areSuggestionsSimilar_trueForSimilar() {
- CharSequence[] leftReplies = new CharSequence[] { "first reply", "second reply"};
- CharSequence[] rightReplies = new CharSequence[] { "first reply", "second reply"};
+ List<CharSequence> leftReplies = createReplies("first reply", "second reply");
+ List<CharSequence> rightReplies = createReplies("first reply", "second reply");
List<Notification.Action> leftActions = Arrays.asList(
createAction("firstAction"),
createAction("secondAction"));
@@ -349,8 +362,8 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
@Test
public void areSuggestionsSimilar_falseForDifferentReplies() {
- CharSequence[] leftReplies = new CharSequence[] { "first reply"};
- CharSequence[] rightReplies = new CharSequence[] { "first reply", "second reply"};
+ List<CharSequence> leftReplies = createReplies("first reply");
+ List<CharSequence> rightReplies = createReplies("first reply", "second reply");
List<Notification.Action> leftActions = Arrays.asList(
createAction("firstAction"),
createAction("secondAction"));
@@ -371,8 +384,8 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
@Test
public void areSuggestionsSimilar_falseForDifferentActions() {
- CharSequence[] leftReplies = new CharSequence[] { "first reply", "second reply"};
- CharSequence[] rightReplies = new CharSequence[] { "first reply", "second reply"};
+ List<CharSequence> leftReplies = createReplies("first reply", "second reply");
+ List<CharSequence> rightReplies = createReplies("first reply", "second reply");
List<Notification.Action> leftActions = Arrays.asList(
createAction("firstAction"),
createAction("secondAction"));
@@ -441,11 +454,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
return createActionBuilder(actionTitle, intent).build();
}
- private List<Notification.Action> createActions(String[] actionTitles) {
- List<Notification.Action> actions = new ArrayList<>();
+ private ArrayList<Notification.Action> createActions(String... actionTitles) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
for (String title : actionTitles) {
actions.add(createAction(title));
}
return actions;
}
+
+ private ArrayList<CharSequence> createReplies(CharSequence... replies) {
+ return new ArrayList<>(Arrays.asList(replies));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 8e3e2db5209e..0c6f25786190 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -23,7 +23,6 @@ import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -37,7 +36,6 @@ import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -53,6 +51,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -66,6 +65,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -139,10 +139,10 @@ public class SmartReplyViewTest extends SysuiTestCase {
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text").build();
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getNotification()).thenReturn(mNotification);
- when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
- mEntry = NotificationEntry.buildForTest(sbn);
+
+ mEntry = new NotificationEntryBuilder()
+ .setNotification(mNotification)
+ .build();
mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
@@ -470,7 +470,11 @@ public class SmartReplyViewTest extends SysuiTestCase {
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
SmartReplyView.SmartReplies smartReplies =
- new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
+ new SmartReplyView.SmartReplies(
+ Arrays.asList(choices),
+ input,
+ pendingIntent,
+ fromAssistant);
return mView.inflateRepliesFromRemoteInput(smartReplies, mLogger, mEntry,
useDelayedOnClickListener);
}
@@ -555,7 +559,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
// Add smart replies
Button previous = null;
SmartReplyView.SmartReplies smartReplies =
- new SmartReplyView.SmartReplies(choices, null, null, false);
+ new SmartReplyView.SmartReplies(Arrays.asList(choices), null, null, false);
for (int i = 0; i < choices.length; ++i) {
Button current = SmartReplyView.inflateReplyButton(mView, mContext, i, smartReplies,
null /* SmartReplyController */, null /* NotificationEntry */,
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml
index dc77981e6c95..0ff85fe39c99 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"Сделать вырез в углу"</string>
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"В правом углу"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml
index a02eaf7bdc26..2493da34af1f 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Увеличить вырез вдвое"</string>
+ <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Сверху и снизу"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml
index 1d1656d2862e..89ac1c3e1b11 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Сделать вырез выше"</string>
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Сверху"</string>
</resources>
diff --git a/proto/src/gnss.proto b/proto/src/gnss.proto
index 1509fc00fb1d..33dbb26c3958 100644
--- a/proto/src/gnss.proto
+++ b/proto/src/gnss.proto
@@ -48,6 +48,27 @@ message GnssLog {
// Hardware revision (EVT, DVT, PVT etc.)
optional string hardware_revision = 13;
+
+ // Total number of sv status messages processed
+ optional int32 num_sv_status_processed = 14;
+
+ // Total number of L5 sv status messages processed
+ optional int32 num_l5_sv_status_processed = 15;
+
+ // Total number of sv status messages processed, where sv is used in fix
+ optional int32 num_sv_status_used_in_fix = 16;
+
+ // Total number of L5 sv status messages processed, where sv is used in fix
+ optional int32 num_l5_sv_status_used_in_fix = 17;
+
+ // Number of l5 top 4 average CN0 processed
+ optional int32 num_l5_top_four_average_cn0_processed = 18;
+
+ // Mean of l5 top 4 average CN0 (dB-Hz)
+ optional double mean_l5_top_four_average_cn0_db_hz = 19;
+
+ // Standard deviation of l5 top 4 average CN0 (dB-Hz)
+ optional double standard_deviation_l5_top_four_average_cn0_db_hz = 20;
}
// Power metrics
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5a4892c75d9e..f40a1eea255b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7414,6 +7414,14 @@ message MetricsEvent {
// Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog
SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748;
+
+
+ // ACTION: Chooser > User taps a system-provided target such as copy
+ // SUBTYPE: Index of target
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: Q - QPR1
+ ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET = 1749;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 1d936f263cb8..cdb062d1963c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -352,7 +352,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public List<AccessibilityWindowInfo> getWindows() {
- ensureWindowsAvailableTimed();
+ ensureWindowsAvailableTimed(Display.DEFAULT_DISPLAY);
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
@@ -362,8 +362,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!permissionGranted) {
return null;
}
- // TODO [Multi-Display] (b/134891479) :
- // using correct display Id to replace DEFAULT_DISPLAY.
List<AccessibilityWindowInfo> internalWindowList =
mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
if (internalWindowList == null) {
@@ -387,7 +385,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- ensureWindowsAvailableTimed();
+ int displayId = Display.INVALID_DISPLAY;
+ synchronized (mLock) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
+ mSystemSupport.getCurrentUserIdLocked(), windowId);
+ }
+ }
+ ensureWindowsAvailableTimed(displayId);
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
@@ -1308,28 +1313,28 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
/**
* Request that the system make sure windows are available to interrogate.
+ *
+ * @param displayId The logical display id.
*/
- private void ensureWindowsAvailableTimed() {
+ private void ensureWindowsAvailableTimed(int displayId) {
synchronized (mLock) {
- // TODO [Multi-Display] (b/134891479) :
- // using correct display Id to replace DEFAULT_DISPLAY.
- if (mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) != null) {
+ if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
return;
}
// If we have no registered callback, update the state we
// we may have to register one but it didn't happen yet.
- if (!mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
// Invokes client change to make sure tracking window enabled.
mSystemSupport.onClientChangeLocked(false);
}
// We have no windows but do not care about them, done.
- if (!mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
return;
}
// Wait for the windows with a timeout.
final long startMillis = SystemClock.uptimeMillis();
- while (mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) == null) {
+ while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
if (remainMillis <= 0) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ddf5bbec7eb1..feb7329bdda6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -580,17 +580,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// Make sure clients receiving this event will be able to get the
// current state of the windows as the window manager may be delaying
// the computation for performance reasons.
- // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY.
boolean shouldComputeWindows = false;
+ int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
+ final int windowId = event.getWindowId();
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
+ && windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
+ mCurrentUserId, windowId);
+ }
+ if (displayId != Display.INVALID_DISPLAY
+ && mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
}
if (shouldComputeWindows) {
- WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class);
- wm.computeWindowsForAccessibility(Display.DEFAULT_DISPLAY);
+ final WindowManagerInternal wm = LocalServices.getService(
+ WindowManagerInternal.class);
+ wm.computeWindowsForAccessibility(displayId);
}
synchronized (mLock) {
notifyAccessibilityServicesDelayedLocked(event, false);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 82a593cdfd7f..0038a27db384 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1463,6 +1463,20 @@ public class AccessibilityWindowManager {
}
/**
+ * Returns the display ID according to given userId and windowId.
+ *
+ * @param userId The userId
+ * @param windowId The windowId
+ * @return The display ID
+ */
+ public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
+ final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
+
+ return displayId;
+ }
+
+ /**
* Gets current input focused window token from window manager, and returns its windowId.
*
* @param userId The userId
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 39866a72ab98..fea4e9047f83 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -19,6 +19,7 @@ package com.android.server.appwidget;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.UserIdInt;
@@ -856,13 +857,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
ArrayList<PendingHostUpdate> outUpdates = new ArrayList<>(N);
LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
for (int i = 0; i < N; i++) {
- if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
- // We key the updates based on request id, so that the values are sorted in the
- // order they were received.
- int M = updatesMap.size();
- for (int j = 0; j < M; j++) {
- outUpdates.add(updatesMap.valueAt(j));
- }
+ updatesMap.clear();
+ host.getPendingUpdatesForId(appWidgetIds[i], updatesMap);
+ // We key the updates based on request id, so that the values are sorted in the
+ // order they were received.
+ int m = updatesMap.size();
+ for (int j = 0; j < m; j++) {
+ outUpdates.add(updatesMap.valueAt(j));
}
}
// Reset the update counter once all the updates have been calculated
@@ -2102,6 +2103,40 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
+ private void scheduleNotifyAppWidgetRemovedLocked(Widget widget) {
+ long requestId = UPDATE_COUNTER.incrementAndGet();
+ if (widget != null) {
+ widget.updateSequenceNos.clear();
+ }
+ if (widget == null || widget.provider == null || widget.provider.zombie
+ || widget.host.callbacks == null || widget.host.zombie) {
+ return;
+ }
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = widget.host;
+ args.arg2 = widget.host.callbacks;
+ args.arg3 = requestId;
+ args.argi1 = widget.appWidgetId;
+
+ mCallbackHandler.obtainMessage(
+ CallbackHandler.MSG_NOTIFY_APP_WIDGET_REMOVED,
+ args).sendToTarget();
+ }
+
+ private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId,
+ long requestId) {
+ try {
+ callbacks.appWidgetRemoved(appWidgetId);
+ host.lastWidgetUpdateSequenceNo = requestId;
+ } catch (RemoteException re) {
+ synchronized (mLock) {
+ Slog.e(TAG, "Widget host dead: " + host.id, re);
+ host.callbacks = null;
+ }
+ }
+ }
+
private void scheduleNotifyGroupHostsForProvidersChangedLocked(int userId) {
final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
@@ -2870,8 +2905,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
*/
void removeWidgetLocked(Widget widget) {
mWidgets.remove(widget);
-
onWidgetRemovedLocked(widget);
+ scheduleNotifyAppWidgetRemovedLocked(widget);
}
private void onWidgetRemovedLocked(Widget widget) {
@@ -3587,6 +3622,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3;
public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4;
+ public static final int MSG_NOTIFY_APP_WIDGET_REMOVED = 5;
public CallbackHandler(Looper looper) {
super(looper, null, false);
@@ -3619,6 +3655,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
handleNotifyProviderChanged(host, callbacks, appWidgetId, info, requestId);
} break;
+ case MSG_NOTIFY_APP_WIDGET_REMOVED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ Host host = (Host) args.arg1;
+ IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+ long requestId = (Long) args.arg3;
+ final int appWidgetId = args.argi1;
+ args.recycle();
+ handleNotifyAppWidgetRemoved(host, callbacks, appWidgetId, requestId);
+ } break;
+
case MSG_NOTIFY_PROVIDERS_CHANGED: {
SomeArgs args = (SomeArgs) message.obj;
Host host = (Host) args.arg1;
@@ -4017,14 +4063,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
/**
* Adds all pending updates in {@param outUpdates} keys by the update time.
*/
- public boolean getPendingUpdatesForId(int appWidgetId,
+ public void getPendingUpdatesForId(int appWidgetId,
LongSparseArray<PendingHostUpdate> outUpdates) {
long updateSequenceNo = lastWidgetUpdateSequenceNo;
int N = widgets.size();
for (int i = 0; i < N; i++) {
Widget widget = widgets.get(i);
if (widget.appWidgetId == appWidgetId) {
- outUpdates.clear();
for (int j = widget.updateSequenceNos.size() - 1; j >= 0; j--) {
long requestId = widget.updateSequenceNos.valueAt(j);
if (requestId <= updateSequenceNo) {
@@ -4046,10 +4091,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
outUpdates.put(requestId, update);
}
- return true;
+ return;
}
}
- return false;
+ outUpdates.put(lastWidgetUpdateSequenceNo,
+ PendingHostUpdate.appWidgetRemoved(appWidgetId));
}
@Override
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index ec2d5454210b..01b40fbff201 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -110,7 +110,7 @@ public class PerformAdbRestoreTask implements Runnable {
if (MORE_DEBUG) {
Slog.v(TAG, "Done consuming input tarfile.");
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to read restore input");
} finally {
try {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5089ee0ace57..69f226f67aa6 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -21,9 +21,7 @@ import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationManager.PASSIVE_PROVIDER;
-import static android.location.LocationProvider.AVAILABLE;
import static android.os.PowerManager.locationPowerSaveModeToString;
-import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
@@ -1088,34 +1086,6 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.decreaseIndent();
}
- @GuardedBy("mLock")
- public long getStatusUpdateTimeLocked() {
- if (mProvider != null) {
- long identity = Binder.clearCallingIdentity();
- try {
- return mProvider.getStatusUpdateTime();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- return 0;
- }
- }
-
- @GuardedBy("mLock")
- public int getStatusLocked(Bundle extras) {
- if (mProvider != null) {
- long identity = Binder.clearCallingIdentity();
- try {
- return mProvider.getStatus(extras);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- return AVAILABLE;
- }
- }
-
@Override
public void onReportLocation(Location location) {
synchronized (mLock) {
@@ -1324,18 +1294,6 @@ public class LocationManagerService extends ILocationManager.Stub {
super.setRequest(request, workSource);
mCurrentRequest = request;
}
-
- @GuardedBy("mLock")
- public void setStatusLocked(int status, Bundle extras, long updateTime) {
- if (mProvider != null) {
- long identity = Binder.clearCallingIdentity();
- try {
- ((MockProvider) mProvider).setStatus(status, extras, updateTime);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
}
/**
@@ -1516,34 +1474,6 @@ public class LocationManagerService extends ILocationManager.Stub {
throw new IllegalStateException("Request for non-existent listener");
}
- public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
- if (mListener != null) {
- try {
- mListener.onStatusChanged(provider, status, extras);
- // call this after broadcasting so we do not increment
- // if we throw an exception.
- incrementPendingBroadcastsLocked();
- } catch (RemoteException e) {
- return false;
- }
- } else {
- Intent statusChanged = new Intent();
- statusChanged.putExtras(new Bundle(extras));
- statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
- try {
- mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
- getResolutionPermission(mAllowedResolutionLevel),
- PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
- // call this after broadcasting so we do not increment
- // if we throw an exception.
- incrementPendingBroadcastsLocked();
- } catch (PendingIntent.CanceledException e) {
- return false;
- }
- }
- return true;
- }
-
public boolean callLocationChangedLocked(Location location) {
if (mListener != null) {
try {
@@ -2296,7 +2226,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Receiver mReceiver;
private boolean mIsForegroundUid;
private Location mLastFixBroadcast;
- private long mLastStatusBroadcast;
private Throwable mStackTrace; // for debugging only
/**
@@ -3406,26 +3335,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- // TODO: location provider status callbacks have been disabled and deprecated, and are
- // guarded behind this setting now. should be removed completely post-Q
- if (Settings.Global.getInt(mContext.getContentResolver(),
- LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
- long newStatusUpdateTime = provider.getStatusUpdateTimeLocked();
- Bundle extras = new Bundle();
- int status = provider.getStatusLocked(extras);
-
- long prevStatusUpdateTime = r.mLastStatusBroadcast;
- if ((newStatusUpdateTime > prevStatusUpdateTime)
- && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
-
- r.mLastStatusBroadcast = newStatusUpdateTime;
- if (!receiver.callStatusChangedLocked(provider.getName(), status, extras)) {
- receiverDead = true;
- Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
- }
- }
- }
-
// track expired records
if (r.mRealRequest.getNumUpdates() <= 0 || r.mRealRequest.getExpireAt() < now) {
if (deadUpdateRecords == null) {
@@ -3625,23 +3534,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public void setTestProviderStatus(String providerName, int status, Bundle extras,
- long updateTime, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
- return;
- }
-
- synchronized (mLock) {
- LocationProvider testProvider = getLocationProviderLocked(providerName);
- if (testProvider == null || !testProvider.isMock()) {
- throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
- }
-
- ((MockLocationProvider) testProvider).setStatusLocked(status, extras, updateTime);
- }
- }
-
- @Override
@NonNull
public List<LocationRequest> getTestProviderCurrentRequests(String providerName,
String opPackageName) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 3fbb21e1bb48..8c60f1aa439b 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -34,6 +34,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.Xml;
@@ -80,10 +81,14 @@ public class PackageWatchdog {
"watchdog_explicit_health_check_enabled";
// Duration to count package failures before it resets to 0
- private static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS =
+ @VisibleForTesting
+ static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS =
(int) TimeUnit.MINUTES.toMillis(1);
// Number of package failures within the duration above before we notify observers
- private static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5;
+ @VisibleForTesting
+ static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5;
+ @VisibleForTesting
+ static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
// Whether explicit health checks are enabled or not
private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
@@ -204,7 +209,7 @@ public class PackageWatchdog {
synchronized (mLock) {
ObserverInternal internalObserver = mAllObservers.get(observer.getName());
if (internalObserver != null) {
- internalObserver.mRegisteredObserver = observer;
+ internalObserver.registeredObserver = observer;
}
}
}
@@ -222,8 +227,10 @@ public class PackageWatchdog {
* check state will be reset to a default depending on if the package is contained in
* {@link mPackagesWithExplicitHealthCheckEnabled}.
*
- * @throws IllegalArgumentException if {@code packageNames} is empty
- * or {@code durationMs} is less than 1
+ * <p>If {@code packageNames} is empty, this will be a no-op.
+ *
+ * <p>If {@code durationMs} is less than 1, a default monitoring duration
+ * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
*/
public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
long durationMs) {
@@ -232,9 +239,9 @@ public class PackageWatchdog {
return;
}
if (durationMs < 1) {
- // TODO: Instead of failing, monitor for default? 48hrs?
- throw new IllegalArgumentException("Invalid duration " + durationMs + "ms for observer "
+ Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
+ observer.getName() + ". Not observing packages " + packageNames);
+ durationMs = DEFAULT_OBSERVING_DURATION_MS;
}
List<MonitoredPackage> packages = new ArrayList<>();
@@ -307,7 +314,7 @@ public class PackageWatchdog {
// Find observer with least user impact
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
ObserverInternal observer = mAllObservers.valueAt(oIndex);
- PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
+ PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null
&& observer.onPackageFailureLocked(
versionedPackage.getPackageName())) {
@@ -458,7 +465,7 @@ public class PackageWatchdog {
synchronized (mLock) {
for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
ObserverInternal observer = mAllObservers.valueAt(observerIdx);
- MonitoredPackage monitoredPackage = observer.mPackages.get(packageName);
+ MonitoredPackage monitoredPackage = observer.packages.get(packageName);
if (monitoredPackage != null) {
int oldState = monitoredPackage.getHealthCheckStateLocked();
@@ -487,7 +494,7 @@ public class PackageWatchdog {
Slog.d(TAG, "Received supported packages " + supportedPackages);
Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
while (oit.hasNext()) {
- Iterator<MonitoredPackage> pit = oit.next().mPackages.values().iterator();
+ Iterator<MonitoredPackage> pit = oit.next().packages.values().iterator();
while (pit.hasNext()) {
MonitoredPackage monitoredPackage = pit.next();
String packageName = monitoredPackage.getName();
@@ -520,7 +527,7 @@ public class PackageWatchdog {
while (oit.hasNext()) {
ObserverInternal observer = oit.next();
Iterator<MonitoredPackage> pit =
- observer.mPackages.values().iterator();
+ observer.packages.values().iterator();
while (pit.hasNext()) {
MonitoredPackage monitoredPackage = pit.next();
String packageName = monitoredPackage.getName();
@@ -578,7 +585,7 @@ public class PackageWatchdog {
private long getNextStateSyncMillisLocked() {
long shortestDurationMs = Long.MAX_VALUE;
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
- ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages;
+ ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).packages;
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
MonitoredPackage mp = packages.valueAt(pIndex);
long duration = mp.getShortestScheduleDurationMsLocked();
@@ -612,8 +619,8 @@ public class PackageWatchdog {
if (!failedPackages.isEmpty()) {
onHealthCheckFailed(observer, failedPackages);
}
- if (observer.mPackages.isEmpty()) {
- Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired");
+ if (observer.packages.isEmpty()) {
+ Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired");
it.remove();
}
}
@@ -623,7 +630,7 @@ public class PackageWatchdog {
Set<MonitoredPackage> failedPackages) {
mLongTaskHandler.post(() -> {
synchronized (mLock) {
- PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
+ PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null) {
Iterator<MonitoredPackage> it = failedPackages.iterator();
while (it.hasNext()) {
@@ -677,7 +684,7 @@ public class PackageWatchdog {
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
ObserverInternal observer = ObserverInternal.read(parser, this);
if (observer != null) {
- mAllObservers.put(observer.mName, observer);
+ mAllObservers.put(observer.name, observer);
}
}
} catch (FileNotFoundException e) {
@@ -722,7 +729,7 @@ public class PackageWatchdog {
PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
DEFAULT_TRIGGER_FAILURE_DURATION_MS);
if (mTriggerFailureDurationMs <= 0) {
- mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_COUNT;
+ mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS;
}
setExplicitHealthCheckEnabled(DeviceConfig.getBoolean(
@@ -803,18 +810,17 @@ public class PackageWatchdog {
* <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
* instances of this class.
*/
- //TODO(b/120598832): Remove 'm' from non-private fields
private static class ObserverInternal {
- public final String mName;
+ public final String name;
//TODO(b/120598832): Add getter for mPackages
@GuardedBy("mLock")
- public final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>();
+ public final ArrayMap<String, MonitoredPackage> packages = new ArrayMap<>();
@Nullable
@GuardedBy("mLock")
- public PackageHealthObserver mRegisteredObserver;
+ public PackageHealthObserver registeredObserver;
ObserverInternal(String name, List<MonitoredPackage> packages) {
- mName = name;
+ this.name = name;
updatePackagesLocked(packages);
}
@@ -826,9 +832,9 @@ public class PackageWatchdog {
public boolean writeLocked(XmlSerializer out) {
try {
out.startTag(null, TAG_OBSERVER);
- out.attribute(null, ATTR_NAME, mName);
- for (int i = 0; i < mPackages.size(); i++) {
- MonitoredPackage p = mPackages.valueAt(i);
+ out.attribute(null, ATTR_NAME, name);
+ for (int i = 0; i < packages.size(); i++) {
+ MonitoredPackage p = packages.valueAt(i);
p.writeLocked(out);
}
out.endTag(null, TAG_OBSERVER);
@@ -843,7 +849,7 @@ public class PackageWatchdog {
public void updatePackagesLocked(List<MonitoredPackage> packages) {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
MonitoredPackage p = packages.get(pIndex);
- mPackages.put(p.mName, p);
+ this.packages.put(p.mName, p);
}
}
@@ -860,7 +866,7 @@ public class PackageWatchdog {
@GuardedBy("mLock")
private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) {
Set<MonitoredPackage> failedPackages = new ArraySet<>();
- Iterator<MonitoredPackage> it = mPackages.values().iterator();
+ Iterator<MonitoredPackage> it = packages.values().iterator();
while (it.hasNext()) {
MonitoredPackage p = it.next();
int oldState = p.getHealthCheckStateLocked();
@@ -883,7 +889,7 @@ public class PackageWatchdog {
*/
@GuardedBy("mLock")
public boolean onPackageFailureLocked(String packageName) {
- MonitoredPackage p = mPackages.get(packageName);
+ MonitoredPackage p = packages.get(packageName);
if (p != null) {
return p.onFailureLocked();
}
@@ -968,6 +974,9 @@ public class PackageWatchdog {
class MonitoredPackage {
//TODO(b/120598832): VersionedPackage?
private final String mName;
+ // Times when package failures happen sorted in ascending order
+ @GuardedBy("mLock")
+ private final LongArrayQueue mFailureHistory = new LongArrayQueue();
// One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
// methods that could change the health check state: handleElapsedTimeLocked and
// tryPassHealthCheckLocked
@@ -987,12 +996,6 @@ public class PackageWatchdog {
// of the package, see #getHealthCheckStateLocked
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
- // System uptime of first package failure
- @GuardedBy("mLock")
- private long mUptimeStartMs;
- // Number of failures since mUptimeStartMs
- @GuardedBy("mLock")
- private int mFailures;
MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) {
this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
@@ -1027,20 +1030,17 @@ public class PackageWatchdog {
*/
@GuardedBy("mLock")
public boolean onFailureLocked() {
+ // Sliding window algorithm: find out if there exists a window containing failures >=
+ // mTriggerFailureCount.
final long now = mSystemClock.uptimeMillis();
- final long duration = now - mUptimeStartMs;
- if (duration > mTriggerFailureDurationMs) {
- // TODO(b/120598832): Reseting to 1 is not correct
- // because there may be more than 1 failure in the last trigger window from now
- // This is the RescueParty impl, will leave for now
- mFailures = 1;
- mUptimeStartMs = now;
- } else {
- mFailures++;
+ mFailureHistory.addLast(now);
+ while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) {
+ // Prune values falling out of the window
+ mFailureHistory.removeFirst();
}
- boolean failed = mFailures >= mTriggerFailureCount;
+ boolean failed = mFailureHistory.size() >= mTriggerFailureCount;
if (failed) {
- mFailures = 0;
+ mFailureHistory.clear();
}
return failed;
}
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 1517887efec2..997178e1582b 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -25,15 +25,12 @@ import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.system.ErrnoException;
-import android.system.Os;
import android.util.Slog;
import libcore.io.IoUtils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -288,7 +285,6 @@ public final class RecoverySystemService extends SystemService {
byte[] cmdUtf8 = command.getBytes("UTF-8");
dos.writeInt(cmdUtf8.length);
dos.write(cmdUtf8, 0, cmdUtf8.length);
- dos.flush();
}
// Read the status from the socket.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 09bfb7a1adca..6c42c7f2583c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15262,7 +15262,12 @@ public class ActivityManagerService extends IActivityManager.Stub
final int uid = getUidFromIntent(intent);
if (uid >= 0) {
mBatteryStatsService.removeUid(uid);
- mAppOpsService.uidRemoved(uid);
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ mAppOpsService.resetAllModes(UserHandle.getUserId(uid),
+ intent.getData().getSchemeSpecificPart());
+ } else {
+ mAppOpsService.uidRemoved(uid);
+ }
}
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 95eb2c69024e..e73abd17cc07 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -26,17 +26,12 @@ import android.os.SystemProperties;
import android.system.Os;
import android.system.OsConstants;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Locale;
-import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -61,8 +56,6 @@ public final class MemoryStatUtil {
private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
/** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
- /** Path to debugfs file for the system ion heap. */
- private static final String DEBUG_SYSTEM_ION_HEAP_FILE = "/sys/kernel/debug/ion/heaps/system";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -79,11 +72,6 @@ public final class MemoryStatUtil {
private static final Pattern PROCFS_SWAP_IN_KILOBYTES =
Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
- private static final Pattern ION_HEAP_SIZE_IN_BYTES =
- Pattern.compile("\n\\s*total\\s*(\\d+)\\s*\n");
- private static final Pattern PROCESS_ION_HEAP_SIZE_IN_BYTES =
- Pattern.compile("\n\\s+\\S+\\s+(\\d+)\\s+(\\d+)");
-
private static final int PGFAULT_INDEX = 9;
private static final int PGMAJFAULT_INDEX = 11;
private static final int START_TIME_INDEX = 21;
@@ -126,9 +114,9 @@ public final class MemoryStatUtil {
/**
* Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
- * /proc/PID/status in bytes or 0 if not available.
+ * /proc/PID/status in kilobytes or 0 if not available.
*/
- public static long readRssHighWaterMarkFromProcfs(int pid) {
+ public static int readRssHighWaterMarkFromProcfs(int pid) {
final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
return parseVmHWMFromProcfs(readFileContents(statusPath));
}
@@ -144,26 +132,6 @@ public final class MemoryStatUtil {
return parseCmdlineFromProcfs(readFileContents(path));
}
- /**
- * Reads size of the system ion heap from debugfs.
- *
- * Returns value of the total size in bytes of the system ion heap from
- * /sys/kernel/debug/ion/heaps/system.
- */
- public static long readSystemIonHeapSizeFromDebugfs() {
- return parseIonHeapSizeFromDebugfs(readFileContents(DEBUG_SYSTEM_ION_HEAP_FILE));
- }
-
- /**
- * Reads process allocation sizes on the system ion heap from debugfs.
- *
- * Returns values of allocation sizes in bytes on the system ion heap from
- * /sys/kernel/debug/ion/heaps/system.
- */
- public static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
- return parseProcessIonHeapSizesFromDebugfs(readFileContents(DEBUG_SYSTEM_ION_HEAP_FILE));
- }
-
private static String readFileContents(String path) {
final File file = new File(path);
if (!file.exists()) {
@@ -236,17 +204,15 @@ public final class MemoryStatUtil {
}
/**
- * Parses RSS high watermark out from the contents of the /proc/pid/status file in procfs. The
- * returned value is in bytes.
+ * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
+ * returned value is in kilobytes.
*/
@VisibleForTesting
- static long parseVmHWMFromProcfs(String procStatusContents) {
+ static int parseVmHWMFromProcfs(String procStatusContents) {
if (procStatusContents == null || procStatusContents.isEmpty()) {
return 0;
}
- // Convert value read from /proc/pid/status from kilobytes to bytes.
- return tryParseLong(RSS_HIGH_WATERMARK_IN_KILOBYTES, procStatusContents)
- * BYTES_IN_KILOBYTE;
+ return (int) tryParseLong(RSS_HIGH_WATERMARK_IN_KILOBYTES, procStatusContents);
}
@@ -268,55 +234,6 @@ public final class MemoryStatUtil {
}
/**
- * Parses the ion heap size from the contents of a file under /sys/kernel/debug/ion/heaps in
- * debugfs. The returned value is in bytes.
- */
- @VisibleForTesting
- static long parseIonHeapSizeFromDebugfs(String contents) {
- if (contents == null || contents.isEmpty()) {
- return 0;
- }
- return tryParseLong(ION_HEAP_SIZE_IN_BYTES, contents);
- }
-
- /**
- * Parses per-process allocation sizes on the ion heap from the contents of a file under
- * /sys/kernel/debug/ion/heaps in debugfs.
- */
- @VisibleForTesting
- static List<IonAllocations> parseProcessIonHeapSizesFromDebugfs(String contents) {
- if (contents == null || contents.isEmpty()) {
- return Collections.emptyList();
- }
-
- final Matcher m = PROCESS_ION_HEAP_SIZE_IN_BYTES.matcher(contents);
- final SparseArray<IonAllocations> entries = new SparseArray<>();
- while (m.find()) {
- try {
- final int pid = Integer.parseInt(m.group(1));
- final long sizeInBytes = Long.parseLong(m.group(2));
- IonAllocations allocations = entries.get(pid);
- if (allocations == null) {
- allocations = new IonAllocations();
- entries.put(pid, allocations);
- }
- allocations.pid = pid;
- allocations.totalSizeInBytes += sizeInBytes;
- allocations.count += 1;
- allocations.maxSizeInBytes = Math.max(allocations.maxSizeInBytes, sizeInBytes);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Failed to parse value", e);
- }
- }
-
- final List<IonAllocations> result = new ArrayList<>(entries.size());
- for (int i = 0; i < entries.size(); i++) {
- result.add(entries.valueAt(i));
- }
- return result;
- }
-
- /**
* Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
@@ -353,40 +270,4 @@ public final class MemoryStatUtil {
/** Device time when the processes started. */
public long startTimeNanos;
}
-
- /** Summary information about process ion allocations. */
- public static final class IonAllocations {
- /** PID these allocations belong to. */
- public int pid;
- /** Size of all individual allocations added together. */
- public long totalSizeInBytes;
- /** Number of allocations. */
- public int count;
- /** Size of the largest allocation. */
- public long maxSizeInBytes;
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- IonAllocations that = (IonAllocations) o;
- return pid == that.pid && totalSizeInBytes == that.totalSizeInBytes
- && count == that.count && maxSizeInBytes == that.maxSizeInBytes;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(pid, totalSizeInBytes, count, maxSizeInBytes);
- }
-
- @Override
- public String toString() {
- return "IonAllocations{"
- + "pid=" + pid
- + ", totalSizeInBytes=" + totalSizeInBytes
- + ", count=" + count
- + ", maxSizeInBytes=" + maxSizeInBytes
- + '}';
- }
- }
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e53cbc1b2f5f..146be5aa7044 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -65,7 +65,6 @@ import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
-import android.media.AudioAttributes;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -361,7 +360,7 @@ public class AppOpsService extends IAppOpsService.Stub {
public int startNesting;
public ArrayMap<String, Ops> pkgOps;
- public SparseIntArray opModes;
+ private SparseIntArray opModes;
// true indicates there is an interested observer, false there isn't but it has such an op
public SparseBooleanArray foregroundOps;
@@ -383,6 +382,64 @@ public class AppOpsService extends IAppOpsService.Stub {
&& (pendingState == UID_STATE_CACHED));
}
+ public int getOpModeCount() {
+ return opModes != null ? opModes.size() : 0;
+ }
+
+ public int getOpCodeAt(int index) {
+ return opModes.keyAt(index);
+ }
+
+ public boolean hasOpMode(int code) {
+ return opModes != null && opModes.indexOfKey(code) >= 0;
+ }
+
+ public int getOpMode(int code) {
+ return opModes.get(code);
+ }
+
+ public boolean putOpMode(int code, int mode) {
+ if (mode == AppOpsManager.opToDefaultMode(code)) {
+ return removeOpMode(code);
+ }
+ if (opModes == null) {
+ opModes = new SparseIntArray();
+ }
+ int index = opModes.indexOfKey(code);
+ if (index < 0) {
+ opModes.put(code, mode);
+ return true;
+ }
+ if (opModes.valueAt(index) == mode) {
+ return false;
+ }
+ opModes.setValueAt(index, mode);
+ return true;
+ }
+
+ public boolean removeOpMode(int code) {
+ if (opModes == null) {
+ return false;
+ }
+ int index = opModes.indexOfKey(code);
+ if (index < 0) {
+ return false;
+ }
+ opModes.removeAt(index);
+ if (opModes.size() == 0) {
+ opModes = null;
+ }
+ return true;
+ }
+
+ @Nullable
+ public SparseIntArray cloneOpModes() {
+ if (opModes == null) {
+ return null;
+ }
+ return opModes.clone();
+ }
+
int evalMode(int op, int mode) {
if (mode == AppOpsManager.MODE_FOREGROUND) {
return state <= AppOpsManager.resolveFirstUnrestrictedUidState(op)
@@ -410,14 +467,13 @@ public class AppOpsService extends IAppOpsService.Stub {
public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
SparseBooleanArray which = null;
hasForegroundWatchers = false;
- if (opModes != null) {
- for (int i = opModes.size() - 1; i >= 0; i--) {
- if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
- }
- evalForegroundWatchers(opModes.keyAt(i), watchers, which);
+ for (int i = getOpModeCount() - 1; i >= 0; i--) {
+ int code = getOpCodeAt(i);
+ if (getOpMode(code) == AppOpsManager.MODE_FOREGROUND) {
+ if (which == null) {
+ which = new SparseBooleanArray();
}
+ evalForegroundWatchers(code, watchers, which);
}
}
if (pkgOps != null) {
@@ -826,20 +882,20 @@ public class AppOpsService extends IAppOpsService.Stub {
final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
final String[] changedPkgs = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- ArraySet<ModeCallback> callbacks;
- synchronized (AppOpsService.this) {
- callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO);
- if (callbacks == null) {
- return;
+ for (int code : OPS_RESTRICTED_ON_SUSPEND) {
+ ArraySet<ModeCallback> callbacks;
+ synchronized (AppOpsService.this) {
+ callbacks = mOpModeWatchers.get(code);
+ if (callbacks == null) {
+ continue;
+ }
+ callbacks = new ArraySet<>(callbacks);
}
- callbacks = new ArraySet<>(callbacks);
- }
- for (int i = 0; i < changedUids.length; i++) {
- final int changedUid = changedUids[i];
- final String changedPkg = changedPkgs[i];
- // We trust packagemanager to insert matching uid and packageNames in the
- // extras
- for (int code : OPS_RESTRICTED_ON_SUSPEND) {
+ for (int i = 0; i < changedUids.length; i++) {
+ final int changedUid = changedUids[i];
+ final String changedPkg = changedPkgs[i];
+ // We trust packagemanager to insert matching uid and packageNames in the
+ // extras
notifyOpChanged(callbacks, code, changedUid, changedPkg);
}
}
@@ -1056,24 +1112,28 @@ public class AppOpsService extends IAppOpsService.Stub {
return resOps;
}
- private ArrayList<AppOpsManager.OpEntry> collectOps(SparseIntArray uidOps, int[] ops) {
- if (uidOps == null) {
+ @Nullable
+ private ArrayList<AppOpsManager.OpEntry> collectOps(@NonNull UidState uidState,
+ @Nullable int[] ops) {
+ int opModeCount = uidState.getOpModeCount();
+ if (opModeCount == 0) {
return null;
}
ArrayList<AppOpsManager.OpEntry> resOps = null;
if (ops == null) {
resOps = new ArrayList<>();
- for (int j=0; j<uidOps.size(); j++) {
- resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j)));
+ for (int i = 0; i < opModeCount; i++) {
+ int code = uidState.getOpCodeAt(i);
+ resOps.add(new OpEntry(code, uidState.getOpMode(code)));
}
} else {
- for (int j=0; j<ops.length; j++) {
- int index = uidOps.indexOfKey(ops[j]);
- if (index >= 0) {
+ for (int i = 0; i < ops.length; i++) {
+ int code = ops[i];
+ if (uidState.hasOpMode(code)) {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j)));
+ resOps.add(new OpEntry(code, uidState.getOpMode(code)));
}
}
}
@@ -1219,11 +1279,11 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null) {
return null;
}
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(uidState.opModes, ops);
+ ArrayList<AppOpsManager.OpEntry> resOps = collectOps(uidState, ops);
if (resOps == null) {
return null;
}
- ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
+ ArrayList<AppOpsManager.PackageOps> res = new ArrayList<>();
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
null, uidState.uid, resOps);
res.add(resPackage);
@@ -1291,29 +1351,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
uidState = new UidState(uid);
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
+ uidState.putOpMode(code, mode);
mUidStates.put(uid, uidState);
scheduleWriteLocked();
- } else if (uidState.opModes == null) {
- if (mode != defaultMode) {
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
- scheduleWriteLocked();
- }
} else {
- if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
- return;
- }
- if (mode == defaultMode) {
- uidState.opModes.delete(code);
- if (uidState.opModes.size() <= 0) {
- uidState.opModes = null;
- }
- } else {
- uidState.opModes.put(code, mode);
+ boolean changed = uidState.putOpMode(code, mode);
+ if (changed) {
+ scheduleWriteLocked();
}
- scheduleWriteLocked();
}
uidState.evalForegroundOps(mOpModeWatchers);
}
@@ -1552,16 +1597,13 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- SparseIntArray opModes = uidState.opModes;
- if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
- final int uidOpCount = opModes.size();
- for (int j = uidOpCount - 1; j >= 0; j--) {
- final int code = opModes.keyAt(j);
+ if (uidState.uid == reqUid || reqUid == -1) {
+ for (int opModeIndex = uidState.getOpModeCount() - 1; opModeIndex >= 0;
+ opModeIndex--) {
+ final int code = uidState.getOpCodeAt(opModeIndex);
+
if (AppOpsManager.opAllowsReset(code)) {
- opModes.removeAt(j);
- if (opModes.size() <= 0) {
- uidState.opModes = null;
- }
+ uidState.removeOpMode(code);
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mOpModeWatchers.get(code));
@@ -1811,9 +1853,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
- if (uidState != null && uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0) {
- final int rawMode = uidState.opModes.get(code);
+ if (uidState != null && uidState.hasOpMode(code)) {
+ final int rawMode = uidState.getOpMode(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, false, false);
@@ -1982,8 +2023,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.hasOpMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getOpMode(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -2284,8 +2325,8 @@ public class AppOpsService extends IAppOpsService.Stub {
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
final int opCode = op.op;
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.hasOpMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getOpMode(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
@@ -2604,9 +2645,8 @@ public class AppOpsService extends IAppOpsService.Stub {
|| !callback.isWatchingUid(uidState.uid)) {
continue;
}
- boolean doAllPackages = uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0
- && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND;
+ boolean doAllPackages = uidState.hasOpMode(code)
+ && uidState.getOpMode(code) == AppOpsManager.MODE_FOREGROUND;
if (uidState.pkgOps != null) {
for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
@@ -2812,9 +2852,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
+ if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
+ return false;
+ }
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- return ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)
- && pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
+ return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
}
private boolean isOpRestrictedLocked(int uid, int code, String packageName,
@@ -2927,12 +2969,9 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null) {
continue;
}
- if (uidState.opModes != null) {
- final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
- if (idx >= 0) {
- uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uidState.opModes.valueAt(idx));
- }
+ if (uidState.hasOpMode(AppOpsManager.OP_RUN_IN_BACKGROUND)) {
+ uidState.putOpMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.getOpMode(
+ AppOpsManager.OP_RUN_IN_BACKGROUND));
}
if (uidState.pkgOps == null) {
continue;
@@ -2988,10 +3027,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
UidState uidState = getUidStateLocked(uid, true);
- if (uidState.opModes == null) {
- uidState.opModes = new SparseIntArray();
- }
- uidState.opModes.put(code, mode);
+ uidState.putOpMode(code, mode);
} else {
Slog.w(TAG, "Unknown element under <uid-ops>: "
+ parser.getName());
@@ -3143,47 +3179,38 @@ public class AppOpsService extends IAppOpsService.Stub {
out.startTag(null, "app-ops");
out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
- SparseArray<SparseIntArray> uidStatesClone;
+ final SparseArray<SparseIntArray> uidOpModes = new SparseArray<>();
synchronized (this) {
- uidStatesClone = new SparseArray<>(mUidStates.size());
-
- final int uidStateCount = mUidStates.size();
- for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
- UidState uidState = mUidStates.valueAt(uidStateNum);
- int uid = mUidStates.keyAt(uidStateNum);
-
- SparseIntArray opModes = uidState.opModes;
- if (opModes != null && opModes.size() > 0) {
- uidStatesClone.put(uid, new SparseIntArray(opModes.size()));
-
- final int opCount = opModes.size();
- for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
- uidStatesClone.get(uid).put(
- opModes.keyAt(opCountNum),
- opModes.valueAt(opCountNum));
- }
+ final int uidStatesSize = mUidStates.size();
+ for (int i = 0; i < uidStatesSize; i++) {
+ final SparseIntArray opModes = mUidStates.valueAt(i).cloneOpModes();
+ if (opModes != null) {
+ final int uid = mUidStates.keyAt(i);
+ uidOpModes.put(uid, opModes);
}
}
}
- final int uidStateCount = uidStatesClone.size();
- for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
- SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
- if (opModes != null && opModes.size() > 0) {
- out.startTag(null, "uid");
- out.attribute(null, "n",
- Integer.toString(uidStatesClone.keyAt(uidStateNum)));
- final int opCount = opModes.size();
- for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
- final int op = opModes.keyAt(opCountNum);
- final int mode = opModes.valueAt(opCountNum);
- out.startTag(null, "op");
- out.attribute(null, "n", Integer.toString(op));
- out.attribute(null, "m", Integer.toString(mode));
- out.endTag(null, "op");
- }
- out.endTag(null, "uid");
+ final int uidOpModesSize = uidOpModes.size();
+ for (int uidOpModesIndex = 0; uidOpModesIndex < uidOpModesSize; uidOpModesIndex++) {
+ final int uid = uidOpModes.keyAt(uidOpModesIndex);
+ final SparseIntArray opModes = uidOpModes.valueAt(uidOpModesIndex);
+
+ out.startTag(null, "uid");
+ out.attribute(null, "n", Integer.toString(uid));
+
+ final int opModesSize = opModes.size();
+ for (int opModesIndex = 0; opModesIndex < opModesSize; opModesIndex++) {
+ final int code = opModes.keyAt(opModesIndex);
+ final int mode = opModes.valueAt(opModesIndex);
+
+ out.startTag(null, "op");
+ out.attribute(null, "n", Integer.toString(code));
+ out.attribute(null, "m", Integer.toString(mode));
+ out.endTag(null, "op");
}
+
+ out.endTag(null, "uid");
}
if (allOps != null) {
@@ -4128,21 +4155,22 @@ public class AppOpsService extends IAppOpsService.Stub {
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
- final SparseIntArray opModes = uidState.opModes;
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
continue;
}
if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
- boolean hasOp = dumpOp < 0 || (uidState.opModes != null
- && uidState.opModes.indexOfKey(dumpOp) >= 0);
+ boolean hasOp = dumpOp < 0 || uidState.hasOpMode(dumpOp);
boolean hasPackage = dumpPackage == null;
boolean hasMode = dumpMode < 0;
- if (!hasMode && opModes != null) {
- for (int opi = 0; !hasMode && opi < opModes.size(); opi++) {
- if (opModes.valueAt(opi) == dumpMode) {
+ if (!hasMode) {
+ int opModeCount = uidState.getOpModeCount();
+ for (int opModeIndex = 0; opModeIndex < opModeCount; opModeIndex++) {
+ int code = uidState.getOpCodeAt(opModeIndex);
+ if (uidState.getOpMode(code) == dumpMode) {
hasMode = true;
+ break;
}
}
}
@@ -4209,20 +4237,18 @@ public class AppOpsService extends IAppOpsService.Stub {
}
needSep = true;
- if (opModes != null) {
- final int opModeCount = opModes.size();
- for (int j = 0; j < opModeCount; j++) {
- final int code = opModes.keyAt(j);
- final int mode = opModes.valueAt(j);
- if (dumpOp >= 0 && dumpOp != code) {
- continue;
- }
- if (dumpMode >= 0 && dumpMode != mode) {
- continue;
- }
- pw.print(" "); pw.print(AppOpsManager.opToName(code));
- pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
+ final int opModeCount = uidState.getOpModeCount();
+ for (int opModeIndex = 0; opModeIndex < opModeCount; opModeIndex++) {
+ final int code = uidState.getOpCodeAt(opModeIndex);
+ final int mode = uidState.getOpMode(code);
+ if (dumpOp >= 0 && dumpOp != code) {
+ continue;
+ }
+ if (dumpMode >= 0 && dumpMode != mode) {
+ continue;
}
+ pw.print(" "); pw.print(AppOpsManager.opToName(code));
+ pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
}
if (pkgOps == null) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index ace0a7d03b85..5983785d4247 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -28,6 +28,7 @@ import android.app.AppOpsManager.UidState;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
@@ -36,6 +37,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.LongSparseArray;
@@ -44,6 +46,7 @@ import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.os.AtomicDirectory;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -272,6 +275,10 @@ final class HistoricalRegistry {
void dump(String prefix, PrintWriter pw, int filterUid,
String filterPackage, int filterOp) {
+ if (!isApiEnabled()) {
+ return;
+ }
+
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
pw.println();
@@ -324,6 +331,11 @@ final class HistoricalRegistry {
void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
@OpFlags int flags, @NonNull RemoteCallback callback) {
+ if (!isApiEnabled()) {
+ callback.sendResult(new Bundle());
+ return;
+ }
+
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
if (!isPersistenceInitializedMLocked()) {
@@ -344,6 +356,11 @@ final class HistoricalRegistry {
void getHistoricalOps(int uid, @NonNull String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
@OpFlags int flags, @NonNull RemoteCallback callback) {
+ if (!isApiEnabled()) {
+ callback.sendResult(new Bundle());
+ return;
+ }
+
final long currentTimeMillis = System.currentTimeMillis();
if (endTimeMillis == Long.MAX_VALUE) {
endTimeMillis = currentTimeMillis;
@@ -681,6 +698,12 @@ final class HistoricalRegistry {
}
}
+ private static boolean isApiEnabled() {
+ return Binder.getCallingUid() == Process.myUid()
+ || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+ }
+
private static final class Persistence {
private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5bc2261878b6..066e765e6e30 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -845,7 +845,12 @@ public class AudioService extends IAudioService.Stub
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
synchronized (mHdmiClientLock) {
+ mHdmiCecSink = false;
mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
+ if (mHdmiManager != null) {
+ mHdmiManager.addHdmiControlStatusChangeListener(
+ mHdmiControlStatusChangeListenerCallback);
+ }
mHdmiTvClient = mHdmiManager.getTvClient();
if (mHdmiTvClient != null) {
mFixedVolumeDevices &= ~AudioSystem.DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER;
@@ -856,7 +861,6 @@ public class AudioService extends IAudioService.Stub
mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_HDMI;
mFullVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
}
- mHdmiCecSink = false;
mHdmiAudioSystemClient = mHdmiManager.getAudioSystemClient();
}
}
@@ -1113,8 +1117,7 @@ public class AudioService extends IAudioService.Stub
checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller);
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiPlaybackClient != null) {
- mHdmiCecSink = false;
- mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
+ updateHdmiCecSinkLocked(mHdmiCecSink | false);
}
}
}
@@ -1124,7 +1127,7 @@ public class AudioService extends IAudioService.Stub
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- mHdmiCecSink = false;
+ updateHdmiCecSinkLocked(mHdmiCecSink | false);
}
}
}
@@ -1902,16 +1905,9 @@ public class AudioService extends IAudioService.Stub
}
}
- if (mHdmiAudioSystemClient != null &&
- mHdmiSystemAudioSupported &&
- streamTypeAlias == AudioSystem.STREAM_MUSIC &&
- (oldIndex != newIndex || isMuteAdjust)) {
- final long identity = Binder.clearCallingIdentity();
- mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
- isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
- getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
- isStreamMute(AudioSystem.STREAM_MUSIC));
- Binder.restoreCallingIdentity(identity);
+ if (streamTypeAlias == AudioSystem.STREAM_MUSIC
+ && (oldIndex != newIndex || isMuteAdjust)) {
+ maybeSendSystemAudioStatusCommand(isMuteAdjust);
}
}
}
@@ -1922,12 +1918,35 @@ public class AudioService extends IAudioService.Stub
// Called after a delay when volume down is pressed while muted
private void onUnmuteStream(int stream, int flags) {
- VolumeStreamState streamState = mStreamStates[stream];
- streamState.mute(false);
+ boolean wasMuted;
+ synchronized (VolumeStreamState.class) {
+ final VolumeStreamState streamState = mStreamStates[stream];
+ wasMuted = streamState.mute(false); // if unmuting causes a change, it was muted
- final int device = getDeviceForStream(stream);
- final int index = mStreamStates[stream].getIndex(device);
- sendVolumeUpdate(stream, index, index, flags, device);
+ final int device = getDeviceForStream(stream);
+ final int index = streamState.getIndex(device);
+ sendVolumeUpdate(stream, index, index, flags, device);
+ }
+ if (stream == AudioSystem.STREAM_MUSIC && wasMuted) {
+ synchronized (mHdmiClientLock) {
+ maybeSendSystemAudioStatusCommand(true);
+ }
+ }
+ }
+
+ @GuardedBy("mHdmiClientLock")
+ private void maybeSendSystemAudioStatusCommand(boolean isMuteAdjust) {
+ if (mHdmiAudioSystemClient == null
+ || !mHdmiSystemAudioSupported) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+ isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
+ getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+ isStreamMute(AudioSystem.STREAM_MUSIC));
+ Binder.restoreCallingIdentity(identity);
}
private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
@@ -2340,17 +2359,9 @@ public class AudioService extends IAudioService.Stub
}
}
synchronized (mHdmiClientLock) {
- if (mHdmiManager != null &&
- mHdmiAudioSystemClient != null &&
- mHdmiSystemAudioSupported &&
- streamTypeAlias == AudioSystem.STREAM_MUSIC &&
- (oldIndex != index)) {
- final long identity = Binder.clearCallingIdentity();
- mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
- false, getStreamVolume(AudioSystem.STREAM_MUSIC),
- getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
- isStreamMute(AudioSystem.STREAM_MUSIC));
- Binder.restoreCallingIdentity(identity);
+ if (streamTypeAlias == AudioSystem.STREAM_MUSIC
+ && (oldIndex != index)) {
+ maybeSendSystemAudioStatusCommand(false);
}
}
sendVolumeUpdate(streamType, oldIndex, index, flags, device);
@@ -4680,7 +4691,12 @@ public class AudioService extends IAudioService.Stub
}
}
- public void mute(boolean state) {
+ /**
+ * Mute/unmute the stream
+ * @param state the new mute state
+ * @return true if the mute state was changed
+ */
+ public boolean mute(boolean state) {
boolean changed = false;
synchronized (VolumeStreamState.class) {
if (state != mIsMuted) {
@@ -4705,6 +4721,7 @@ public class AudioService extends IAudioService.Stub
intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
sendBroadcastToAll(intent);
}
+ return changed;
}
public int getStreamType() {
@@ -5774,32 +5791,37 @@ public class AudioService extends IAudioService.Stub
// are transformed into key events for the HDMI playback client.
//==========================================================================================
- private class MyDisplayStatusCallback implements HdmiPlaybackClient.DisplayStatusCallback {
- public void onComplete(int status) {
- synchronized (mHdmiClientLock) {
- if (mHdmiManager != null) {
- mHdmiCecSink = (status != HdmiControlManager.POWER_STATUS_UNKNOWN);
- // Television devices without CEC service apply software volume on HDMI output
- if (mHdmiCecSink) {
- if (DEBUG_VOL) {
- Log.d(TAG, "CEC sink: setting HDMI as full vol device");
- }
- mFullVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
- } else {
- if (DEBUG_VOL) {
- Log.d(TAG, "TV, no CEC: setting HDMI as regular vol device");
- }
- // Android TV devices without CEC service apply software volume on
- // HDMI output
- mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_HDMI;
- }
- checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
- "HdmiPlaybackClient.DisplayStatusCallback");
- }
+ @GuardedBy("mHdmiClientLock")
+ private void updateHdmiCecSinkLocked(boolean hdmiCecSink) {
+ mHdmiCecSink = hdmiCecSink;
+ if (mHdmiCecSink) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "CEC sink: setting HDMI as full vol device");
}
+ mFullVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
+ } else {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "TV, no CEC: setting HDMI as regular vol device");
+ }
+ // Android TV devices without CEC service apply software volume on
+ // HDMI output
+ mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_HDMI;
}
+
+ checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
+ "HdmiPlaybackClient.DisplayStatusCallback");
}
+ private class MyHdmiControlStatusChangeListenerCallback
+ implements HdmiControlManager.HdmiControlStatusChangeListener {
+ public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager == null) return;
+ updateHdmiCecSinkLocked(isCecEnabled ? isCecAvailable : false);
+ }
+ }
+ };
+
private final Object mHdmiClientLock = new Object();
// If HDMI-CEC system audio is supported
@@ -5815,12 +5837,14 @@ public class AudioService extends IAudioService.Stub
@GuardedBy("mHdmiClientLock")
private HdmiPlaybackClient mHdmiPlaybackClient;
// true if we are a set-top box, an HDMI sink is connected and it supports CEC.
+ @GuardedBy("mHdmiClientLock")
private boolean mHdmiCecSink;
// Set only when device is an audio system.
@GuardedBy("mHdmiClientLock")
private HdmiAudioSystemClient mHdmiAudioSystemClient;
- private MyDisplayStatusCallback mHdmiDisplayStatusCallback = new MyDisplayStatusCallback();
+ private MyHdmiControlStatusChangeListenerCallback mHdmiControlStatusChangeListenerCallback =
+ new MyHdmiControlStatusChangeListenerCallback();
@Override
public int setHdmiSystemAudioSupported(boolean on) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index faf01f981e3e..7b3eae14c97a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2082,7 +2082,6 @@ public class Vpn {
}
out.write(0xFF);
out.write(0xFF);
- out.flush();
// Wait for End-of-File.
InputStream in = mSockets[i].getInputStream();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2b849d69ae1c..3856de47a522 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -42,6 +42,7 @@ import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
+import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.IHdmiInputChangeListener;
@@ -252,6 +253,11 @@ public class HdmiControlService extends SystemService {
// Type of logical devices hosted in the system. Stored in the unmodifiable list.
private final List<Integer> mLocalDevices;
+ // List of records for HDMI control status change listener for death monitoring.
+ @GuardedBy("mLock")
+ private final ArrayList<HdmiControlStatusChangeListenerRecord>
+ mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
+
// List of records for hotplug event listener to handle the the caller killed in action.
@GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -581,6 +587,7 @@ public class HdmiControlService extends SystemService {
}
if (reason != -1) {
invokeVendorCommandListenersOnControlStateChanged(true, reason);
+ announceHdmiControlStatusChange(true);
}
}
@@ -1370,6 +1377,37 @@ public class HdmiControlService extends SystemService {
// Record class that monitors the event of the caller of being killed. Used to clean up
// the listener list and record list accordingly.
+ private final class HdmiControlStatusChangeListenerRecord implements IBinder.DeathRecipient {
+ private final IHdmiControlStatusChangeListener mListener;
+
+ HdmiControlStatusChangeListenerRecord(IHdmiControlStatusChangeListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mHdmiControlStatusChangeListenerRecords.remove(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof HdmiControlStatusChangeListenerRecord)) return false;
+ if (obj == this) return true;
+ HdmiControlStatusChangeListenerRecord other =
+ (HdmiControlStatusChangeListenerRecord) obj;
+ return other.mListener == this.mListener;
+ }
+
+ @Override
+ public int hashCode() {
+ return mListener.hashCode();
+ }
+ }
+
+ // Record class that monitors the event of the caller of being killed. Used to clean up
+ // the listener list and record list accordingly.
private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
private final IHdmiHotplugEventListener mListener;
@@ -1695,6 +1733,20 @@ public class HdmiControlService extends SystemService {
}
@Override
+ public void addHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.addHdmiControlStatusChangeListener(listener);
+ }
+
+ @Override
+ public void removeHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.removeHdmiControlStatusChangeListener(listener);
+ }
+
+ @Override
public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
HdmiControlService.this.addHotplugEventListener(listener);
@@ -2218,6 +2270,51 @@ public class HdmiControlService extends SystemService {
source.queryDisplayStatus(callback);
}
+ private void addHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ final HdmiControlStatusChangeListenerRecord record =
+ new HdmiControlStatusChangeListenerRecord(listener);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died");
+ return;
+ }
+ synchronized (mLock) {
+ mHdmiControlStatusChangeListenerRecords.add(record);
+ }
+
+ // Inform the listener of the initial state of each HDMI port by generating
+ // hotplug events.
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (!mHdmiControlStatusChangeListenerRecords.contains(record)) return;
+ }
+
+ // Return the current status of mHdmiControlEnabled;
+ synchronized (mLock) {
+ invokeHdmiControlStatusChangeListenerLocked(listener, mHdmiControlEnabled);
+ }
+ }
+ });
+ }
+
+ private void removeHdmiControlStatusChangeListener(
+ final IHdmiControlStatusChangeListener listener) {
+ synchronized (mLock) {
+ for (HdmiControlStatusChangeListenerRecord record :
+ mHdmiControlStatusChangeListenerRecords) {
+ if (record.mListener.asBinder() == listener.asBinder()) {
+ listener.asBinder().unlinkToDeath(record, 0);
+ mHdmiControlStatusChangeListenerRecords.remove(record);
+ break;
+ }
+ }
+ }
+ }
+
private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
try {
@@ -2450,6 +2547,47 @@ public class HdmiControlService extends SystemService {
}
}
+ private void announceHdmiControlStatusChange(boolean isEnabled) {
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ for (HdmiControlStatusChangeListenerRecord record :
+ mHdmiControlStatusChangeListenerRecords) {
+ invokeHdmiControlStatusChangeListenerLocked(record.mListener, isEnabled);
+ }
+ }
+ }
+
+ private void invokeHdmiControlStatusChangeListenerLocked(
+ IHdmiControlStatusChangeListener listener, boolean isEnabled) {
+ if (isEnabled) {
+ queryDisplayStatus(new IHdmiControlCallback.Stub() {
+ public void onComplete(int status) {
+ boolean isAvailable = true;
+ if (status == HdmiControlManager.POWER_STATUS_UNKNOWN
+ || status == HdmiControlManager.RESULT_EXCEPTION
+ || status == HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE) {
+ isAvailable = false;
+ }
+
+ try {
+ listener.onStatusChange(isEnabled, isAvailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report HdmiControlStatusChange: " + isEnabled
+ + " isAvailable: " + isAvailable, e);
+ }
+ }
+ });
+ return;
+ }
+
+ try {
+ listener.onStatusChange(isEnabled, false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report HdmiControlStatusChange: " + isEnabled
+ + " isAvailable: " + false, e);
+ }
+ }
+
public HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
@@ -2819,6 +2957,8 @@ public class HdmiControlService extends SystemService {
disableHdmiControlService();
}
});
+ announceHdmiControlStatusChange(enabled);
+
return;
}
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index c1a63940c080..b05742af04ee 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -18,7 +18,6 @@ package com.android.server.location;
import android.content.Context;
import android.location.Location;
-import android.location.LocationProvider;
import android.os.Bundle;
import android.os.WorkSource;
@@ -132,26 +131,4 @@ public abstract class AbstractLocationProvider {
* thread.
*/
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- /**
- * Invoked by the location service to retrieve the current status of the provider. May be
- * invoked from any thread.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public int getStatus(Bundle extras) {
- return LocationProvider.AVAILABLE;
- }
-
- /**
- * Invoked by the location service to retrieve the last update time of the status of the
- * provider. May be invoked from any thread.
- *
- * @deprecated Will be removed in a future release.
- */
- @Deprecated
- public long getStatusUpdateTime() {
- return 0;
- }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 461f19bf8b08..c6226fa98197 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -37,7 +37,6 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
-import android.location.LocationProvider;
import android.location.LocationRequest;
import android.os.AsyncTask;
import android.os.BatteryStats;
@@ -279,15 +278,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private final Object mLock = new Object();
- // current status
- private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
-
- // time for last status update
- private long mStatusUpdateTime = SystemClock.elapsedRealtime();
-
- // turn off GPS fix icon if we haven't received a fix in 10 seconds
- private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
-
// stop trying if we do not receive a fix within 60 seconds
private static final int NO_FIX_TIMEOUT = 60 * 1000;
@@ -485,7 +475,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
- subscriptionOrCarrierConfigChanged(context);
+ subscriptionOrCarrierConfigChanged();
break;
}
}
@@ -500,7 +490,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mGnssMetrics.resetConstellationTypes();
}
- private void subscriptionOrCarrierConfigChanged(Context context) {
+ private void subscriptionOrCarrierConfigChanged() {
if (DEBUG) Log.d(TAG, "received SIM related action: ");
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -1010,24 +1000,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
@Override
- public int getStatus(Bundle extras) {
- mLocationExtras.setBundle(extras);
- return mStatus;
- }
-
- private void updateStatus(int status) {
- if (status != mStatus) {
- mStatus = status;
- mStatusUpdateTime = SystemClock.elapsedRealtime();
- }
- }
-
- @Override
- public long getStatusUpdateTime() {
- return mStatusUpdateTime;
- }
-
- @Override
public void onSetRequest(ProviderRequest request, WorkSource source) {
sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
@@ -1266,7 +1238,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
// reset SV count to zero
- updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
mLocationExtras.reset();
mFixRequestTime = SystemClock.elapsedRealtime();
if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
@@ -1290,7 +1261,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mLastPositionMode = null;
// reset SV count to zero
- updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
mLocationExtras.reset();
}
}
@@ -1381,7 +1351,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
}
- if (mStarted && mStatus != LocationProvider.AVAILABLE) {
+ if (mStarted) {
// For devices that use framework scheduling, a timer may be set to ensure we don't
// spend too much power searching for a location, when the requested update rate is
// slow.
@@ -1389,8 +1359,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
mAlarmManager.cancel(mTimeoutIntent);
}
-
- updateStatus(LocationProvider.AVAILABLE);
}
if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
@@ -1458,7 +1426,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
info.mSvCarrierFreqs);
// Log CN0 as part of GNSS metrics
- mGnssMetrics.logCn0(info.mCn0s, info.mSvCount);
+ mGnssMetrics.logCn0(info.mCn0s, info.mSvCount, info.mSvCarrierFreqs);
if (VERBOSE) {
Log.v(TAG, "SV count: " + info.mSvCount);
@@ -1505,10 +1473,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
// return number of sats used in fix instead of total reported
mLocationExtras.set(usedInFixCount, meanCn0, maxCn0);
- if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
- SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) {
- updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
- }
+ mGnssMetrics.logSvStatus(info.mSvCount, info.mSvidWithFlags, info.mSvCarrierFreqs);
}
@NativeEntryPoint
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 09911ff1a74e..694f14904668 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
-import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -190,22 +189,6 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
@Override
- public int getStatus(Bundle extras) {
- return mServiceWatcher.runOnBinderBlocking(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- return service.getStatus(extras);
- }, LocationProvider.TEMPORARILY_UNAVAILABLE);
- }
-
- @Override
- public long getStatusUpdateTime() {
- return mServiceWatcher.runOnBinderBlocking(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- return service.getStatusUpdateTime();
- }, 0L);
- }
-
- @Override
public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index b0c4c2e65fd8..472876bfd86a 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -19,8 +19,6 @@ package com.android.server.location;
import android.annotation.Nullable;
import android.content.Context;
import android.location.Location;
-import android.location.LocationProvider;
-import android.os.Bundle;
import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
@@ -38,9 +36,6 @@ public class MockProvider extends AbstractLocationProvider {
private boolean mEnabled;
@Nullable private Location mLocation;
- private int mStatus;
- private long mStatusUpdateTime;
- private Bundle mExtras;
public MockProvider(Context context,
LocationProviderManager locationProviderManager, ProviderProperties properties) {
@@ -48,9 +43,6 @@ public class MockProvider extends AbstractLocationProvider {
mEnabled = true;
mLocation = null;
- mStatus = LocationProvider.AVAILABLE;
- mStatusUpdateTime = 0;
- mExtras = null;
setProperties(properties);
}
@@ -72,13 +64,6 @@ public class MockProvider extends AbstractLocationProvider {
}
}
- /** Sets the status for this mock provider. */
- public void setStatus(int status, Bundle extras, long updateTime) {
- mStatus = status;
- mStatusUpdateTime = updateTime;
- mExtras = extras;
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("last location=" + mLocation);
@@ -86,19 +71,4 @@ public class MockProvider extends AbstractLocationProvider {
@Override
public void onSetRequest(ProviderRequest request, WorkSource source) {}
-
- @Override
- public int getStatus(Bundle extras) {
- if (mExtras != null) {
- extras.clear();
- extras.putAll(mExtras);
- }
-
- return mStatus;
- }
-
- @Override
- public long getStatusUpdateTime() {
- return mStatusUpdateTime;
- }
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 64477a52a9d9..aed29272cada 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -38,6 +38,9 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -81,6 +84,39 @@ public class ConditionProviders extends ManagedServices {
}
@Override
+ protected ArrayMap<Boolean, ArrayList<ComponentName>>
+ resetComponents(String packageName, int userId) {
+ resetPackage(packageName, userId);
+ ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
+ changes.put(true, new ArrayList<>(0));
+ changes.put(false, new ArrayList<>(0));
+ return changes;
+ }
+
+ /**
+ * @return true if the passed package is enabled. false otherwise
+ */
+ boolean resetPackage(String packageName, int userId) {
+ boolean isAllowed = super.isPackageOrComponentAllowed(packageName, userId);
+ boolean isDefault = super.isDefaultComponentOrPackage(packageName);
+ if (!isAllowed && isDefault) {
+ setPackageOrComponentEnabled(packageName, userId, true, true);
+ }
+ if (isAllowed && !isDefault) {
+ setPackageOrComponentEnabled(packageName, userId, true, false);
+ }
+ return !isAllowed && isDefault;
+ }
+
+ @Override
+ void writeDefaults(XmlSerializer out) throws IOException {
+ synchronized (mDefaultsLock) {
+ String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages);
+ out.attribute(null, ATT_DEFAULTS, defaults);
+ }
+ }
+
+ @Override
protected Config getConfig() {
final Config c = new Config();
c.caption = "condition provider";
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4828bbfff676..48b0fd6e86d1 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -105,6 +105,7 @@ abstract public class ManagedServices {
static final String ATT_USER_ID = "user";
static final String ATT_IS_PRIMARY = "primary";
static final String ATT_VERSION = "version";
+ static final String ATT_DEFAULTS = "defaults";
static final int DB_VERSION = 1;
@@ -128,6 +129,10 @@ abstract public class ManagedServices {
*/
private final ArrayList<Pair<ComponentName, Integer>> mServicesBound = new ArrayList<>();
private final ArraySet<Pair<ComponentName, Integer>> mServicesRebinding = new ArraySet<>();
+ // we need these packages to be protected because classes that inherit from it need to see it
+ protected final Object mDefaultsLock = new Object();
+ protected final ArraySet<ComponentName> mDefaultComponents = new ArraySet<>();
+ protected final ArraySet<String> mDefaultPackages = new ArraySet<>();
// lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
@@ -179,6 +184,112 @@ abstract public class ManagedServices {
}
}
+ protected void addDefaultComponentOrPackage(String packageOrComponent) {
+ if (packageOrComponent != null) {
+ synchronized (mDefaultsLock) {
+ ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
+ if (cn == null) {
+ mDefaultPackages.add(packageOrComponent);
+ } else {
+ mDefaultPackages.add(cn.getPackageName());
+ mDefaultComponents.add(cn);
+ }
+ }
+ }
+ }
+
+ boolean isDefaultComponentOrPackage(String packageOrComponent) {
+ synchronized (mDefaultsLock) {
+ ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
+ if (cn == null) {
+ return mDefaultPackages.contains(packageOrComponent);
+ } else {
+ return mDefaultComponents.contains(cn);
+ }
+ }
+ }
+
+ ArraySet<ComponentName> getDefaultComponents() {
+ synchronized (mDefaultsLock) {
+ return new ArraySet<>(mDefaultComponents);
+ }
+ }
+
+ ArraySet<String> getDefaultPackages() {
+ synchronized (mDefaultsLock) {
+ return new ArraySet<>(mDefaultPackages);
+ }
+ }
+
+ /**
+ * When resetting a package, we need to enable default components that belong to that packages
+ * we also need to disable components that are not default to return the managed service state
+ * to when a new android device is first turned on for that package.
+ *
+ * @param packageName package to reset.
+ * @param userId the android user id
+ * @return a list of components that were permitted
+ */
+ @NonNull
+ ArrayMap<Boolean, ArrayList<ComponentName>> resetComponents(String packageName, int userId) {
+ // components that we want to enable
+ ArrayList<ComponentName> componentsToEnable =
+ new ArrayList<>(mDefaultComponents.size());
+
+ // components that were removed
+ ArrayList<ComponentName> disabledComponents =
+ new ArrayList<>(mDefaultComponents.size());
+
+ // all components that are enabled now
+ ArraySet<ComponentName> enabledComponents =
+ new ArraySet<>(getAllowedComponents(userId));
+
+ boolean changed = false;
+
+ synchronized (mDefaultsLock) {
+ // record all components that are enabled but should not be by default
+ for (int i = 0; i < mDefaultComponents.size() && enabledComponents.size() > 0; i++) {
+ ComponentName currentDefault = mDefaultComponents.valueAt(i);
+ if (packageName.equals(currentDefault.getPackageName())
+ && !enabledComponents.contains(currentDefault)) {
+ componentsToEnable.add(currentDefault);
+ }
+ }
+ synchronized (mApproved) {
+ final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(
+ userId);
+ if (approvedByType != null) {
+ final int M = approvedByType.size();
+ for (int j = 0; j < M; j++) {
+ final ArraySet<String> approved = approvedByType.valueAt(j);
+ for (int i = 0; i < enabledComponents.size(); i++) {
+ ComponentName currentComponent = enabledComponents.valueAt(i);
+ if (packageName.equals(currentComponent.getPackageName())
+ && !mDefaultComponents.contains(currentComponent)) {
+ if (approved.remove(currentComponent.flattenToString())) {
+ disabledComponents.add(currentComponent);
+ changed = true;
+ }
+ }
+ }
+ for (int i = 0; i < componentsToEnable.size(); i++) {
+ ComponentName candidate = componentsToEnable.get(i);
+ changed |= approved.add(candidate.flattenToString());
+ }
+ }
+
+ }
+ }
+ }
+ if (changed) rebindServices(false, USER_ALL);
+
+ ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
+ changes.put(true, componentsToEnable);
+ changes.put(false, disabledComponents);
+
+ return changes;
+ }
+
protected int getBindFlags() {
return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT;
}
@@ -310,11 +421,24 @@ abstract public class ManagedServices {
}
}
+ void writeDefaults(XmlSerializer out) throws IOException {
+ synchronized (mDefaultsLock) {
+ List<String> componentStrings = new ArrayList<>(mDefaultComponents.size());
+ for (int i = 0; i < mDefaultComponents.size(); i++) {
+ componentStrings.add(mDefaultComponents.valueAt(i).flattenToString());
+ }
+ String defaults = String.join(ENABLED_SERVICES_SEPARATOR, componentStrings);
+ out.attribute(null, ATT_DEFAULTS, defaults);
+ }
+ }
+
public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
out.startTag(null, getConfig().xmlTag);
out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+ writeDefaults(out);
+
if (forBackup) {
trimApprovedListsAccordingToInstalledServices(userId);
}
@@ -378,6 +502,27 @@ abstract public class ManagedServices {
loadAllowedComponentsFromSettings();
}
+ void readDefaults(XmlPullParser parser) {
+ String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
+ if (defaultComponents == null) {
+ return;
+ }
+ String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR);
+ synchronized (mDefaultsLock) {
+ for (int i = 0; i < components.length; i++) {
+ if (!TextUtils.isEmpty(components[i])) {
+ ComponentName cn = ComponentName.unflattenFromString(components[i]);
+ if (cn != null) {
+ mDefaultPackages.add(cn.getPackageName());
+ mDefaultComponents.add(cn);
+ } else {
+ mDefaultPackages.add(components[i]);
+ }
+ }
+ }
+ }
+ }
+
public void readXml(
XmlPullParser parser,
TriPredicate<String, Integer, String> allowedManagedServicePackages,
@@ -386,6 +531,7 @@ abstract public class ManagedServices {
throws XmlPullParserException, IOException {
// read grants
int type;
+ readDefaults(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
if (type == XmlPullParser.END_TAG
@@ -785,9 +931,9 @@ abstract public class ManagedServices {
return queryPackageForServices(packageName, 0, userId);
}
- protected Set<ComponentName> queryPackageForServices(String packageName, int extraFlags,
+ protected ArraySet<ComponentName> queryPackageForServices(String packageName, int extraFlags,
int userId) {
- Set<ComponentName> installed = new ArraySet<>();
+ ArraySet<ComponentName> installed = new ArraySet<>();
final PackageManager pm = mContext.getPackageManager();
Intent queryIntent = new Intent(mConfig.serviceInterface);
if (!TextUtils.isEmpty(packageName)) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f71b3627135d..2d4c6cf70847 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -356,7 +356,6 @@ public class NotificationManagerService extends SystemService {
private static final int REQUEST_CODE_TIMEOUT = 1;
private static final String SCHEME_TIMEOUT = "timeout";
private static final String EXTRA_KEY = "key";
-
private IActivityManager mAm;
private ActivityManager mActivityManager;
private IPackageManager mPackageManager;
@@ -524,24 +523,21 @@ public class NotificationManagerService extends SystemService {
}
- protected void readDefaultApprovedServices(int userId) {
+
+ void loadDefaultApprovedServices(int userId) {
String defaultListenerAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultListenerAccessPackages);
if (defaultListenerAccess != null) {
- for (String whitelisted :
- defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
- // Gather all notification listener components for candidate pkgs.
- Set<ComponentName> approvedListeners =
- mListeners.queryPackageForServices(whitelisted,
+ String[] listeners =
+ defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < listeners.length; i++) {
+ ArraySet<ComponentName> approvedListeners =
+ mListeners.queryPackageForServices(listeners[i],
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE, userId);
- for (ComponentName cn : approvedListeners) {
- try {
- getBinderService().setNotificationListenerAccessGrantedForUser(cn,
- userId, true);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ for (int k = 0; k < approvedListeners.size(); k++) {
+ ComponentName cn = approvedListeners.valueAt(k);
+ mListeners.addDefaultComponentOrPackage(cn.flattenToString());
}
}
}
@@ -549,46 +545,86 @@ public class NotificationManagerService extends SystemService {
String defaultDndAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultDndAccessPackages);
if (defaultDndAccess != null) {
- for (String whitelisted :
- defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
- try {
- getBinderService().setNotificationPolicyAccessGranted(whitelisted, true);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
+ String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
+ for (int i = 0; i < dnds.length; i++) {
+ mConditionProviders.addDefaultComponentOrPackage(dnds[i]);
}
}
+
+ ArraySet<String> assistants = new ArraySet<>();
+ String deviceAssistant = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE);
+ if (deviceAssistant != null) {
+ assistants.addAll(Arrays.asList(deviceAssistant.split(
+ ManagedServices.ENABLED_SERVICES_SEPARATOR)));
+ }
+ assistants.addAll(Arrays.asList(getContext().getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessComponent)
+ .split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
+ for (int i = 0; i < assistants.size(); i++) {
+ String cnString = assistants.valueAt(i);
+ mAssistants.addDefaultComponentOrPackage(cnString);
+ }
+ }
+
+ protected void allowDefaultApprovedServices(int userId) {
+
+ ArraySet<ComponentName> defaultListeners = mListeners.getDefaultComponents();
+ for (int i = 0; i < defaultListeners.size(); i++) {
+ ComponentName cn = defaultListeners.valueAt(i);
+ allowNotificationListener(userId, cn);
+ }
+
+ ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages();
+ for (int i = 0; i < defaultDnds.size(); i++) {
+ allowDndPackage(defaultDnds.valueAt(i));
+ }
+
setDefaultAssistantForUser(userId);
}
protected void setDefaultAssistantForUser(int userId) {
- List<ComponentName> validAssistants = new ArrayList<>(
- mAssistants.queryPackageForServices(
- null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId));
+ ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents();
+ // We should have only one default assistant by default
+ // allowAssistant should execute once in practice
+ for (int i = 0; i < defaults.size(); i++) {
+ ComponentName cn = defaults.valueAt(i);
+ if (allowAssistant(userId, cn)) return;
+ }
+ }
- List<String> candidateStrs = new ArrayList<>();
- candidateStrs.add(DeviceConfig.getProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE));
- candidateStrs.add(getContext().getResources().getString(
- com.android.internal.R.string.config_defaultAssistantAccessComponent));
+ private void allowDndPackage(String packageName) {
+ try {
+ getBinderService().setNotificationPolicyAccessGranted(packageName, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
- for (String candidateStr : candidateStrs) {
- if (TextUtils.isEmpty(candidateStr)) {
- continue;
- }
- ComponentName candidate = ComponentName.unflattenFromString(candidateStr);
- if (candidate != null && validAssistants.contains(candidate)) {
- setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true);
- Slog.d(TAG, String.format("Set default NAS to be %s in %d", candidateStr, userId));
- return;
- } else {
- Slog.w(TAG, "Invalid default NAS config is found: " + candidateStr);
- }
+ private void allowNotificationListener(int userId, ComponentName cn) {
+
+ try {
+ getBinderService().setNotificationListenerAccessGrantedForUser(cn,
+ userId, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
}
+ private boolean allowAssistant(int userId, ComponentName candidate) {
+ Set<ComponentName> validAssistants =
+ mAssistants.queryPackageForServices(
+ null,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (candidate != null && validAssistants.contains(candidate)) {
+ setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true);
+ return true;
+ }
+ return false;
+ }
+
void readPolicyXml(InputStream stream, boolean forRestore, int userId)
throws XmlPullParserException, NumberFormatException, IOException {
final XmlPullParser parser = Xml.newPullParser();
@@ -656,7 +692,8 @@ public class NotificationManagerService extends SystemService {
} catch (FileNotFoundException e) {
// No data yet
// Load default managed services approvals
- readDefaultApprovedServices(USER_SYSTEM);
+ loadDefaultApprovedServices(USER_SYSTEM);
+ allowDefaultApprovedServices(USER_SYSTEM);
} catch (IOException e) {
Log.wtf(TAG, "Unable to read notification policy", e);
} catch (NumberFormatException e) {
@@ -1363,7 +1400,7 @@ public class NotificationManagerService extends SystemService {
if (userId != USER_NULL) {
mUserProfiles.updateCache(context);
if (!mUserProfiles.isManagedProfile(userId)) {
- readDefaultApprovedServices(userId);
+ allowDefaultApprovedServices(userId);
}
}
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
@@ -1741,7 +1778,6 @@ public class NotificationManagerService extends SystemService {
mPolicyFile = policyFile;
loadPolicyFile();
-
mStatusBar = getLocalService(StatusBarManagerInternal.class);
if (mStatusBar != null) {
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -2957,21 +2993,52 @@ public class NotificationManagerService extends SystemService {
@Override
public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
+ boolean packagesChanged = false;
checkCallerIsSystem();
-
// Cancel posted notifications
+ final int userId = UserHandle.getUserId(uid);
cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
- final String[] packages = new String[] {packageName};
- final int[] uids = new int[] {uid};
+ // Zen
+ packagesChanged |=
+ mConditionProviders.resetPackage(packageName, userId);
- // Listener & assistant
- mListeners.onPackagesChanged(true, packages, uids);
- mAssistants.onPackagesChanged(true, packages, uids);
+ // Listener
+ ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
+ mListeners.resetComponents(packageName, userId);
+ packagesChanged |= changedListeners.get(true).size() > 0
+ || changedListeners.get(false).size() > 0;
- // Zen
- mConditionProviders.onPackagesChanged(true, packages, uids);
+ // When a listener is enabled, we enable the dnd package as a secondary
+ for (int i = 0; i < changedListeners.get(true).size(); i++) {
+ mConditionProviders.setPackageOrComponentEnabled(
+ changedListeners.get(true).get(i).getPackageName(),
+ userId, false, true);
+ }
+
+ // Assistant
+ ArrayMap<Boolean, ArrayList<ComponentName>> changedAssistants =
+ mAssistants.resetComponents(packageName, userId);
+ packagesChanged |= changedAssistants.get(true).size() > 0
+ || changedAssistants.get(false).size() > 0;
+
+ // we want only one assistant enabled
+ for (int i = 1; i < changedAssistants.get(true).size(); i++) {
+ mAssistants.setPackageOrComponentEnabled(
+ changedAssistants.get(true).get(i).flattenToString(),
+ userId, true, false);
+ }
+
+ // When the default assistant is enabled, we enable the dnd package as a secondary
+ if (changedAssistants.get(true).size() > 0) {
+ //we want only one assistant active
+ mConditionProviders
+ .setPackageOrComponentEnabled(
+ changedAssistants.get(true).get(0).getPackageName(),
+ userId, false, true);
+
+ }
// Snoozing
mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName);
@@ -2981,6 +3048,14 @@ public class NotificationManagerService extends SystemService {
mPreferencesHelper.clearData(packageName, uid);
}
+ if (packagesChanged) {
+ getContext().sendBroadcastAsUser(new Intent(
+ ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+ .setPackage(packageName)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
+ UserHandle.of(userId), null);
+ }
+
handleSavePolicyFile();
}
@@ -5469,8 +5544,16 @@ public class NotificationManagerService extends SystemService {
}
synchronized (mNotificationLock) {
- // Look for the notification, searching both the posted and enqueued lists.
- NotificationRecord r = findNotificationLocked(mPkg, mTag, mId, mUserId);
+ // If the notification is currently enqueued, repost this runnable so it has a
+ // chance to notify listeners
+ if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
+ != null) {
+ mHandler.post(this);
+ return;
+ }
+ // Look for the notification in the posted list, since we already checked enqueued.
+ NotificationRecord r =
+ findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
if (r != null) {
// The notification was found, check if it should be removed.
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 7eb74381f7ae..48678bfac86e 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,13 +16,18 @@
package com.android.server.pm;
+import static android.content.pm.PackageParser.Component;
+import static android.content.pm.PackageParser.IntentInfo;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import android.Manifest;
import android.annotation.Nullable;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -38,6 +43,7 @@ import com.android.server.FgThread;
import com.android.server.compat.PlatformCompat;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -55,13 +61,13 @@ class AppsFilter {
// Forces filtering logic to run for debug purposes.
// STOPSHIP (b/136675067): should be false after development is complete
- private static final boolean DEBUG_RUN_WHEN_DISABLED = true;
+ private static final boolean DEBUG_RUN_WHEN_DISABLED = false;
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
@SuppressWarnings("ConstantExpression")
- private static final boolean DEBUG_LOGGING = false | DEBUG_RUN_WHEN_DISABLED | DEBUG_ALLOW_ALL;
+ private static final boolean DEBUG_LOGGING = false | DEBUG_ALLOW_ALL;
/**
* This contains a list of packages that are implicitly queryable because another app explicitly
@@ -200,23 +206,43 @@ class AppsFilter {
return false;
}
for (Intent intent : querying.mQueriesIntents) {
- for (PackageParser.Activity activity : potentialTarget.activities) {
- if (activity.intents != null) {
- for (PackageParser.ActivityIntentInfo filter : activity.intents) {
- if (matches(intent, filter)) {
- return true;
- }
- }
- }
+ if (matches(intent, potentialTarget.providers, potentialTarget.activities,
+ potentialTarget.services, potentialTarget.receivers)) {
+ return true;
}
}
return false;
}
- /** Returns true if the given intent matches the given filter. */
- private static boolean matches(Intent intent, PackageParser.ActivityIntentInfo filter) {
- return filter.match(intent.getAction(), intent.getType(), intent.getScheme(),
- intent.getData(), intent.getCategories(), "AppsFilter") > 0;
+ private static boolean matches(Intent intent,
+ ArrayList<PackageParser.Provider> providerList,
+ ArrayList<? extends Component<? extends IntentInfo>>... componentLists) {
+ for (int p = providerList.size() - 1; p >= 0; p--) {
+ PackageParser.Provider provider = providerList.get(p);
+ final ProviderInfo providerInfo = provider.info;
+ final Uri data = intent.getData();
+ if ("content".equalsIgnoreCase(intent.getScheme())
+ && data != null
+ && providerInfo.authority.equalsIgnoreCase(data.getAuthority())) {
+ return true;
+ }
+ }
+
+ for (int l = componentLists.length - 1; l >= 0; l--) {
+ ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l];
+ for (int c = components.size() - 1; c >= 0; c--) {
+ Component<? extends IntentInfo> component = components.get(c);
+ ArrayList<? extends IntentInfo> intents = component.intents;
+ for (int i = intents.size() - 1; i >= 0; i--) {
+ IntentFilter intentFilter = intents.get(i);
+ if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
+ intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
/**
@@ -328,9 +354,15 @@ class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
+ if (DEBUG_LOGGING) {
+ Slog.d(TAG, "filtering disabled; skipped");
+ }
return false;
}
if (callingUid < Process.FIRST_APPLICATION_UID) {
+ if (DEBUG_LOGGING) {
+ Slog.d(TAG, "filtering skipped; " + callingUid + " is system");
+ }
return false;
}
if (callingSetting == null) {
@@ -376,14 +408,13 @@ class AppsFilter {
}
if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
if (DEBUG_LOGGING) {
- Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
- + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : "BLOCKED"));
+ log(callingPkgSetting, targetPkgSetting,
+ DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED");
}
return !DEBUG_ALLOW_ALL;
} else {
if (DEBUG_LOGGING) {
- Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
- + targetPkgSetting.name + " DISABLED");
+ log(callingPkgSetting, targetPkgSetting, "DISABLED");
}
return false;
}
@@ -397,38 +428,65 @@ class AppsFilter {
// This package isn't technically installed and won't be written to settings, so we can
// treat it as filtered until it's available again.
if (targetPkg == null) {
+ if (DEBUG_LOGGING) {
+ Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
+ }
return true;
}
final String targetName = targetPkg.packageName;
if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "caller pre-R");
+ }
return false;
}
if (isImplicitlyQueryableSystemApp(targetPkgSetting)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "implicitly queryable sys");
+ }
return false;
}
if (targetPkg.mForceQueryable) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "manifest forceQueryable");
+ }
return false;
}
if (mForceQueryable.contains(targetName)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "whitelist forceQueryable");
+ }
return false;
}
if (mQueriesViaPackage.containsKey(callingName)
&& mQueriesViaPackage.get(callingName).contains(
targetName)) {
// the calling package has explicitly declared the target package; allow
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "queries package");
+ }
return false;
} else if (mQueriesViaIntent.containsKey(callingName)
&& mQueriesViaIntent.get(callingName).contains(targetName)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "queries intent");
+ }
return false;
}
if (mImplicitlyQueryable.get(userId) != null
&& mImplicitlyQueryable.get(userId).containsKey(callingName)
&& mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "implicitly queryable for user");
+ }
return false;
}
if (callingPkgSetting.pkg.instrumentation.size() > 0) {
for (int i = 0, max = callingPkgSetting.pkg.instrumentation.size(); i < max; i++) {
if (callingPkgSetting.pkg.instrumentation.get(i).info.targetPackage == targetName) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ }
return false;
}
}
@@ -437,6 +495,9 @@ class AppsFilter {
if (mPermissionManager.checkPermission(
Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
== PackageManager.PERMISSION_GRANTED) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "permission");
+ }
return false;
}
} catch (RemoteException e) {
@@ -445,6 +506,13 @@ class AppsFilter {
return true;
}
+ private static void log(PackageSetting callingPkgSetting, PackageSetting targetPkgSetting,
+ String description) {
+ Slog.wtf(TAG,
+ "interaction: " + callingPkgSetting.name + " -> " + targetPkgSetting.name + " "
+ + description);
+ }
+
private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) {
return targetPkgSetting.isSystem() && (mSystemAppsQueryable
|| mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
@@ -467,7 +535,9 @@ class AppsFilter {
for (int user : users) {
pw.append(" User ").append(Integer.toString(user)).println(":");
final HashMap<String, Set<String>> queryMapForUser = mImplicitlyQueryable.get(user);
- dumpQueriesMap(pw, filteringPackageName, queryMapForUser, " ");
+ if (queryMapForUser != null) {
+ dumpQueriesMap(pw, filteringPackageName, queryMapForUser, " ");
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9b6333d7bef4..3464cab99d93 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -443,45 +443,39 @@ public class LauncherAppsService extends SystemService {
if (isManagedProfileAdmin(user, appInfo.packageName)) {
return false;
}
- // If app does not have any components or any permissions, the app can legitimately
- // have no icon so we do not show the synthetic activity.
- return hasComponentsAndRequestsPermissions(appInfo.packageName);
- }
-
- private boolean hasComponentsAndRequestsPermissions(@NonNull String packageName) {
final PackageManagerInternal pmInt =
LocalServices.getService(PackageManagerInternal.class);
- final PackageParser.Package pkg = pmInt.getPackage(packageName);
+ final PackageParser.Package pkg = pmInt.getPackage(appInfo.packageName);
if (pkg == null) {
// Should not happen, but we shouldn't be failing if it does
return false;
}
- if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
- return false;
- }
- if (!hasApplicationDeclaredActivities(pkg)
- && ArrayUtils.isEmpty(pkg.receivers)
- && ArrayUtils.isEmpty(pkg.providers)
- && ArrayUtils.isEmpty(pkg.services)) {
- return false;
- }
- return true;
+ // If app does not have any default enabled launcher activity or any permissions,
+ // the app can legitimately have no icon so we do not show the synthetic activity.
+ return requestsPermissions(pkg) && hasDefaultEnableLauncherActivity(
+ appInfo.packageName);
}
- private boolean hasApplicationDeclaredActivities(@NonNull PackageParser.Package pkg) {
- if (pkg.activities == null) {
- return false;
- }
- if (ArrayUtils.isEmpty(pkg.activities)) {
- return false;
- }
- // If it only contains synthetic AppDetailsActivity only, it means application does
- // not have actual activity declared in manifest.
- if (pkg.activities.size() == 1 && PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
- pkg.activities.get(0).className)) {
- return false;
+ private boolean requestsPermissions(@NonNull PackageParser.Package pkg) {
+ return !ArrayUtils.isEmpty(pkg.requestedPermissions);
+ }
+
+ private boolean hasDefaultEnableLauncherActivity(@NonNull String packageName) {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ final Intent matchIntent = new Intent(Intent.ACTION_MAIN);
+ matchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ matchIntent.setPackage(packageName);
+ final List<ResolveInfo> infoList = pmInt.queryIntentActivities(matchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS, Binder.getCallingUid(),
+ getCallingUserId());
+ final int size = infoList.size();
+ for (int i = 0; i < size; i++) {
+ if (infoList.get(i).activityInfo.enabled) {
+ return true;
+ }
}
- return true;
+ return false;
}
private boolean isManagedProfileAdmin(UserHandle user, String packageName) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b124c4b18efa..58596aa0aad2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17931,7 +17931,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (removedAppId >= 0) {
packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
- null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
null, null, broadcastUsers, instantUserIds);
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3f2cadeb527d..3482e92a6fbd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -443,7 +443,8 @@ public class StagingManager {
try {
rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId);
} catch (RemoteException re) {
- // Cannot happen, the rollback manager is in the same process.
+ Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
+ + originalSession.sessionId, re);
}
}
@@ -856,7 +857,8 @@ public class StagingManager {
+ session.sessionId);
}
} catch (RemoteException re) {
- // Cannot happen, the rollback manager is in the same process.
+ Slog.e(TAG, "Failed to notifyStagedSession for session: "
+ + session.sessionId, re);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81723cbe22c5..dd1adb703e62 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2253,10 +2253,11 @@ public class UserManagerService extends IUserManager.Stub {
@GuardedBy({"mPackagesLock", "mRestrictionsLock"})
private void fallbackToSingleUserLP() {
- int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN;
- // In headless system user mode, the primary flag is assigned to the first human user.
+ int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
+ | UserInfo.FLAG_PRIMARY;
+ // In headless system user mode, headless system user is not a full user.
if (!UserManager.isHeadlessSystemUserMode()) {
- flags |= UserInfo.FLAG_PRIMARY | UserInfo.FLAG_FULL;
+ flags |= UserInfo.FLAG_FULL;
}
// Create the system user
UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
@@ -2770,17 +2771,7 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
}
- // In headless system user mode, we assign the first human user the primary flag.
- // And if there is no device owner, we also assign the admin flag to primary user.
- if (UserManager.isHeadlessSystemUserMode()
- && !isGuest && !isManagedProfile && getPrimaryUser() == null) {
- flags |= UserInfo.FLAG_PRIMARY;
- synchronized (mUsersLock) {
- if (!mIsDeviceManaged) {
- flags |= UserInfo.FLAG_ADMIN;
- }
- }
- }
+
if (!isManagedProfile) {
// New users cannot be system, and it's not a profile, so per-force it's FULL.
flags |= UserInfo.FLAG_FULL;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 9948a3ad47da..b3f1867fdb06 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -277,9 +277,6 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
try {
fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
if (fd == null || !fd.getFileDescriptor().valid()) {
- Slog.wtf(TAG,
- "ParcelFileDescriptor.open returned an invalid descriptor for "
- + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
} else {
postSuccess(packageName, fd, callback);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a9e3f046e425..a57321e8012d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -793,62 +793,68 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mLock) {
- if (mCheckPermissionDelegate == null) {
- return checkPermissionImpl(permName, pkgName, userId);
- }
checkPermissionDelegate = mCheckPermissionDelegate;
}
+ if (checkPermissionDelegate == null) {
+ return checkPermissionImpl(permName, pkgName, userId);
+ }
return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
- PermissionManagerService.this::checkPermissionImpl);
+ this::checkPermissionImpl);
}
- private int checkPermissionImpl(String permName, String pkgName, int userId) {
- final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+ private int checkPermissionImpl(@NonNull String permissionName, @NonNull String packageName,
+ @UserIdInt int userId) {
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
if (pkg == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkPermissionInternal(pkg, true, permName, userId);
+ return checkPermissionInternal(pkg, true, permissionName, true, userId)
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
}
- private int checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
- @NonNull String permissionName, @UserIdInt int userId) {
+ private boolean checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
+ @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps,
+ @UserIdInt int userId) {
final int callingUid = getCallingUid();
if (isPackageExplicit || pkg.mSharedUserId == null) {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
- return PackageManager.PERMISSION_DENIED;
+ return false;
}
} else {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
- return PackageManager.PERMISSION_DENIED;
+ return false;
}
}
final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
- return PackageManager.PERMISSION_DENIED;
+ return false;
}
final PermissionsState permissionsState = ps.getPermissionsState();
- if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
- return PackageManager.PERMISSION_GRANTED;
+ if (checkSinglePermissionInternal(uid, permissionsState, permissionName,
+ useRequestedPermissionsForLegacyApps)) {
+ return true;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
- if (fullerPermissionName != null
- && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
- return PackageManager.PERMISSION_GRANTED;
+ if (fullerPermissionName != null && checkSinglePermissionInternal(uid, permissionsState,
+ fullerPermissionName, useRequestedPermissionsForLegacyApps)) {
+ return true;
}
- return PackageManager.PERMISSION_DENIED;
+ return false;
}
private boolean checkSinglePermissionInternal(int uid,
- @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
+ @NonNull PermissionsState permissionsState, @NonNull String permissionName,
+ boolean useRequestedPermissionsForLegacyApps) {
boolean hasPermission = permissionsState.hasPermission(permissionName,
UserHandle.getUserId(uid));
- if (!hasPermission && mSettings.isPermissionRuntime(permissionName)) {
+ if (!hasPermission && useRequestedPermissionsForLegacyApps
+ && mSettings.isPermissionRuntime(permissionName)) {
final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
final int packageNamesSize = packageNames != null ? packageNames.length : 0;
for (int i = 0; i < packageNamesSize; i++) {
@@ -891,12 +897,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
checkPermissionDelegate = mCheckPermissionDelegate;
}
return checkPermissionDelegate.checkUidPermission(permName, uid,
- PermissionManagerService.this::checkUidPermissionImpl);
+ this::checkUidPermissionImpl);
}
- private int checkUidPermissionImpl(String permName, int uid) {
+ private int checkUidPermissionImpl(@NonNull String permissionName, int uid) {
final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
- return checkUidPermissionInternal(pkg, uid, permName);
+ return checkUidPermissionInternal(uid, pkg, permissionName, true)
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
}
/**
@@ -906,24 +913,25 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @see SystemConfig#getSystemPermissions()
*/
- private int checkUidPermissionInternal(@Nullable Package pkg, int uid,
- @NonNull String permissionName) {
+ private boolean checkUidPermissionInternal(int uid, @Nullable Package pkg,
+ @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps) {
if (pkg != null) {
final int userId = UserHandle.getUserId(uid);
- return checkPermissionInternal(pkg, false, permissionName, userId);
+ return checkPermissionInternal(pkg, false, permissionName,
+ useRequestedPermissionsForLegacyApps, userId);
}
if (checkSingleUidPermissionInternal(uid, permissionName)) {
- return PackageManager.PERMISSION_GRANTED;
+ return true;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
&& checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
- return PackageManager.PERMISSION_GRANTED;
+ return true;
}
- return PackageManager.PERMISSION_DENIED;
+ return false;
}
private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
@@ -933,6 +941,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ private int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) {
+ boolean granted = isUidPermissionGranted(uid, permissionName);
+ // TODO: Foreground permissions.
+ return granted ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ }
+
+ private boolean isUidPermissionGranted(int uid, @NonNull String permissionName) {
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
+ return checkUidPermissionInternal(uid, pkg, permissionName, false);
+ }
+
@Override
public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
mContext.enforceCallingOrSelfPermission(
@@ -4446,6 +4465,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback);
}
}
+
+ @Override
+ public int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) {
+ return PermissionManagerService.this.computeRuntimePermissionAppOpMode(uid,
+ permissionName);
+ }
}
private static final class OnPermissionChangeListeners extends Handler {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 04ec5ba04bb6..8f22f9245a53 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -445,4 +445,13 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
/** Called when a new user has been created. */
public abstract void onNewUserCreated(@UserIdInt int userId);
+
+ /**
+ * Compute an app op mode based on its runtime permission state.
+ *
+ * @param uid the uid for the app op
+ * @param permissionName the permission name for the app op
+ * @return the computed mode
+ */
+ public abstract int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName);
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index f68a06bb191a..1bf319dfcb69 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -65,8 +65,8 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
-
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
+
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -309,6 +309,8 @@ public final class PermissionPolicyService extends SystemService {
/* ignore */
}
+ permissionControllerManager.updateUserSensitive();
+
packageManagerInternal.setRuntimePermissionsFingerPrint(Build.FINGERPRINT, userId);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index d5adb5e1c111..47370b644b91 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -1047,8 +1047,14 @@ public abstract class WindowOrientationListener {
@Override
public void onSensorChanged(SensorEvent event) {
int newRotation;
+
+ int reportedRotation = (int) event.values[0];
+ if (reportedRotation < 0 || reportedRotation > 3) {
+ return;
+ }
+
synchronized (mLock) {
- mDesiredRotation = (int) event.values[0];
+ mDesiredRotation = reportedRotation;
newRotation = evaluateRotationChangeLocked();
}
if (newRotation >=0) {
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 4e9b724f528d..ed7d23483be7 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -156,7 +156,7 @@ public class AttentionDetector {
final UserSwitchObserver observer = new UserSwitchObserver();
ActivityManager.getService().registerUserSwitchObserver(observer, TAG);
} catch (RemoteException e) {
- // Shouldn't happen since in-process.
+ // Shouldn't happen since in-process.
}
context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
@@ -172,12 +172,13 @@ public class AttentionDetector {
public long updateUserActivity(long nextScreenDimming) {
if (nextScreenDimming == mLastActedOnNextScreenDimming
|| !mIsSettingEnabled
- || !isAttentionServiceSupported()
|| mWindowManager.isKeyguardShowingAndNotOccluded()) {
return nextScreenDimming;
}
- if (!serviceHasSufficientPermissions()) {
+ if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) {
+ // Turns off adaptive sleep in settings for all users if attention service is not
+ // available. The setting itself should also be grayed out in this case.
Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
return nextScreenDimming;
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 0d5746bf547f..6769fe07bbf8 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -65,7 +65,7 @@ class Rollback {
/**
* The directory where the rollback data is stored.
*/
- public final File backupDir;
+ private final File mBackupDir;
/**
* The time when the upgrade occurred, for purposes of expiring
@@ -74,24 +74,24 @@ class Rollback {
* The timestamp is not applicable for all rollback states, but we make
* sure to keep it non-null to avoid potential errors there.
*/
- public @NonNull Instant timestamp;
+ private @NonNull Instant mTimestamp;
/**
* The session ID for the staged session if this rollback data represents a staged session,
* {@code -1} otherwise.
*/
- public final int stagedSessionId;
+ private final int mStagedSessionId;
/**
* The current state of the rollback.
* ENABLING, AVAILABLE, or COMMITTED.
*/
- public @RollbackState int state;
+ private @RollbackState int mState;
/**
* The id of the post-reboot apk session for a staged install, if any.
*/
- public int apkSessionId = -1;
+ private int mApkSessionId = -1;
/**
* True if we are expecting the package manager to call restoreUserData
@@ -99,7 +99,7 @@ class Rollback {
* has not yet been fully applied.
*/
// NOTE: All accesses to this field are from the RollbackManager handler thread.
- public boolean restoreUserDataInProgress = false;
+ private boolean mRestoreUserDataInProgress = false;
/**
* Constructs a new, empty Rollback instance.
@@ -114,10 +114,10 @@ class Rollback {
/* isStaged */ stagedSessionId != -1,
/* causePackages */ new ArrayList<>(),
/* committedSessionId */ -1);
- this.backupDir = backupDir;
- this.stagedSessionId = stagedSessionId;
- this.state = ROLLBACK_STATE_ENABLING;
- this.timestamp = Instant.now();
+ mBackupDir = backupDir;
+ mStagedSessionId = stagedSessionId;
+ mState = ROLLBACK_STATE_ENABLING;
+ mTimestamp = Instant.now();
}
/**
@@ -126,21 +126,115 @@ class Rollback {
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
@RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
this.info = info;
- this.backupDir = backupDir;
- this.timestamp = timestamp;
- this.stagedSessionId = stagedSessionId;
- this.state = state;
- this.apkSessionId = apkSessionId;
- this.restoreUserDataInProgress = restoreUserDataInProgress;
+ mBackupDir = backupDir;
+ mTimestamp = timestamp;
+ mStagedSessionId = stagedSessionId;
+ mState = state;
+ mApkSessionId = apkSessionId;
+ mRestoreUserDataInProgress = restoreUserDataInProgress;
}
/**
* Whether the rollback is for rollback of a staged install.
*/
- public boolean isStaged() {
+ boolean isStaged() {
return info.isStaged();
}
+ /**
+ * Returns the directory in which rollback data should be stored.
+ */
+ File getBackupDir() {
+ return mBackupDir;
+ }
+
+ /**
+ * Returns the time when the upgrade occurred, for purposes of expiring rollback data.
+ */
+ Instant getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * Sets the time at which upgrade occurred.
+ */
+ void setTimestamp(Instant timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * Returns the session ID for the staged session if this rollback data represents a staged
+ * session, or {@code -1} otherwise.
+ */
+ int getStagedSessionId() {
+ return mStagedSessionId;
+ }
+
+ /**
+ * Returns true if the rollback is in the ENABLING state.
+ */
+ boolean isEnabling() {
+ return mState == ROLLBACK_STATE_ENABLING;
+ }
+
+ /**
+ * Returns true if the rollback is in the AVAILABLE state.
+ */
+ boolean isAvailable() {
+ return mState == ROLLBACK_STATE_AVAILABLE;
+ }
+
+ /**
+ * Returns true if the rollback is in the COMMITTED state.
+ */
+ boolean isCommitted() {
+ return mState == ROLLBACK_STATE_COMMITTED;
+ }
+
+ /**
+ * Sets the state of the rollback to AVAILABLE.
+ */
+ void setAvailable() {
+ mState = ROLLBACK_STATE_AVAILABLE;
+ }
+
+ /**
+ * Sets the state of the rollback to COMMITTED.
+ */
+ void setCommitted() {
+ mState = ROLLBACK_STATE_COMMITTED;
+ }
+
+ /**
+ * Returns the id of the post-reboot apk session for a staged install, if any.
+ */
+ int getApkSessionId() {
+ return mApkSessionId;
+ }
+
+ /**
+ * Sets the id of the post-reboot apk session for a staged install.
+ */
+ void setApkSessionId(int apkSessionId) {
+ mApkSessionId = apkSessionId;
+ }
+
+ /**
+ * Returns true if we are expecting the package manager to call restoreUserData for this
+ * rollback because it has just been committed but the rollback has not yet been fully applied.
+ */
+ boolean isRestoreUserDataInProgress() {
+ return mRestoreUserDataInProgress;
+ }
+
+ /**
+ * Sets whether we are expecting the package manager to call restoreUserData for this
+ * rollback because it has just been committed but the rollback has not yet been fully applied.
+ */
+ void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) {
+ mRestoreUserDataInProgress = restoreUserDataInProgress;
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
@@ -160,7 +254,7 @@ class Rollback {
throw new ParseException("Invalid rollback state: " + state, 0);
}
- public String getStateAsString() {
- return rollbackStateToString(state);
+ String getStateAsString() {
+ return rollbackStateToString(mState);
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 3147bc629ffa..96d284bb1c58 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -282,7 +282,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE) {
+ if (rollback.isAvailable()) {
rollbacks.add(rollback.info);
}
}
@@ -298,7 +298,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
+ if (rollback.isCommitted()) {
rollbacks.add(rollback.info);
}
}
@@ -332,7 +332,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- rollback.timestamp = rollback.timestamp.plusMillis(timeDifference);
+ rollback.setTimestamp(rollback.getTimestamp().plusMillis(timeDifference));
saveRollback(rollback);
}
}
@@ -358,7 +358,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Slog.i(TAG, "Initiating rollback");
Rollback rollback = getRollbackForId(rollbackId);
- if (rollback == null || rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
+ if (rollback == null || !rollback.isAvailable()) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
@@ -454,8 +454,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// TODO: Could this cause a rollback to be
// resurrected if it should otherwise have
// expired by now?
- rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
- rollback.restoreUserDataInProgress = false;
+ rollback.setAvailable();
+ rollback.setRestoreUserDataInProgress(false);
}
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
"Rollback downgrade install failed: "
@@ -468,7 +468,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (!rollback.isStaged()) {
// All calls to restoreUserData should have
// completed by now for a non-staged install.
- rollback.restoreUserDataInProgress = false;
+ rollback.setRestoreUserDataInProgress(false);
}
rollback.info.setCommittedSessionId(parentSessionId);
@@ -490,8 +490,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
);
synchronized (mLock) {
- rollback.state = Rollback.ROLLBACK_STATE_COMMITTED;
- rollback.restoreUserDataInProgress = true;
+ rollback.setCommitted();
+ rollback.setRestoreUserDataInProgress(true);
}
parentSession.commit(receiver.getIntentSender());
} catch (IOException e) {
@@ -618,9 +618,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
if (rollback.isStaged()) {
- if (rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+ if (rollback.isEnabling()) {
enabling.add(rollback);
- } else if (rollback.restoreUserDataInProgress) {
+ } else if (rollback.isRestoreUserDataInProgress()) {
restoreInProgress.add(rollback);
}
@@ -635,8 +635,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (Rollback rollback : enabling) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo session = installer.getSessionInfo(
- rollback.stagedSessionId);
+ PackageInstaller.SessionInfo session =
+ installer.getSessionInfo(rollback.getStagedSessionId());
if (session == null || session.isStagedSessionFailed()) {
// TODO: Do we need to remove this from
// mRollbacks, or is it okay to leave as
@@ -650,13 +650,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (Rollback rollback : restoreInProgress) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo session = installer.getSessionInfo(
- rollback.stagedSessionId);
+ PackageInstaller.SessionInfo session =
+ installer.getSessionInfo(rollback.getStagedSessionId());
// TODO: What if session is null?
if (session != null) {
if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
synchronized (mLock) {
- rollback.restoreUserDataInProgress = false;
+ rollback.setRestoreUserDataInProgress(false);
}
saveRollback(rollback);
}
@@ -694,8 +694,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
while (iter.hasNext()) {
Rollback rollback = iter.next();
// TODO: Should we remove rollbacks in the ENABLING state here?
- if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE
- || rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+ if (rollback.isEnabling() || rollback.isAvailable()) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.getPackageName().equals(packageName)
&& !packageVersionsEqual(
@@ -761,15 +760,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- if (rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
+ if (!rollback.isAvailable()) {
continue;
}
if (!now.isBefore(
- rollback.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
+ rollback.getTimestamp()
+ .plusMillis(mRollbackLifetimeDurationInMillis))) {
iter.remove();
deleteRollback(rollback);
- } else if (oldest == null || oldest.isAfter(rollback.timestamp)) {
- oldest = rollback.timestamp;
+ } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
+ oldest = rollback.getTimestamp();
}
}
}
@@ -877,7 +877,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.apkSessionId == parentSession.getSessionId()) {
+ if (rollback.getApkSessionId() == parentSession.getSessionId()) {
// This is the apk session for a staged session with rollback enabled. We do not
// need to create a new rollback for this session.
return true;
@@ -1020,7 +1020,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.state != Rollback.ROLLBACK_STATE_ENABLING) {
+ if (!rollback.isEnabling()) {
continue;
}
@@ -1053,7 +1053,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
- if (candidate.restoreUserDataInProgress) {
+ if (candidate.isRestoreUserDataInProgress()) {
info = getPackageRollbackInfo(candidate, packageName);
if (info != null) {
rollback = candidate;
@@ -1146,8 +1146,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
- if (candidate.stagedSessionId == originalSessionId) {
- candidate.apkSessionId = apkSessionId;
+ if (candidate.getStagedSessionId() == originalSessionId) {
+ candidate.setApkSessionId(apkSessionId);
rollback = candidate;
break;
}
@@ -1333,8 +1333,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// to a new package being installed. Won't this revive an expired
// rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
synchronized (mLock) {
- rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
- rollback.timestamp = Instant.now();
+ rollback.setAvailable();
+ rollback.setTimestamp(Instant.now());
}
saveRollback(rollback);
@@ -1434,9 +1434,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
ipw.println(info.getRollbackId() + ":");
ipw.increaseIndent();
ipw.println("-state: " + rollback.getStateAsString());
- ipw.println("-timestamp: " + rollback.timestamp);
- if (rollback.stagedSessionId != -1) {
- ipw.println("-stagedSessionId: " + rollback.stagedSessionId);
+ ipw.println("-timestamp: " + rollback.getTimestamp());
+ if (rollback.getStagedSessionId() != -1) {
+ ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
}
ipw.println("-packages:");
ipw.increaseIndent();
@@ -1446,7 +1446,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+ " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
}
ipw.decreaseIndent();
- if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
+ if (rollback.isCommitted()) {
ipw.println("-causePackages:");
ipw.increaseIndent();
for (VersionedPackage cPkg : info.getCausePackages()) {
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index b2448f62bcd7..772c53fec4ce 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -17,7 +17,6 @@
package com.android.server.rollback;
import static com.android.server.rollback.Rollback.rollbackStateFromString;
-import static com.android.server.rollback.Rollback.rollbackStateToString;
import android.annotation.NonNull;
import android.content.pm.VersionedPackage;
@@ -216,7 +215,7 @@ class RollbackStore {
static void backupPackageCodePath(Rollback rollback, String packageName, String codePath)
throws IOException {
File sourceFile = new File(codePath);
- File targetDir = new File(rollback.backupDir, packageName);
+ File targetDir = new File(rollback.getBackupDir(), packageName);
targetDir.mkdirs();
File targetFile = new File(targetDir, sourceFile.getName());
@@ -229,7 +228,7 @@ class RollbackStore {
* Includes the base apk and any splits. Returns null if none found.
*/
static File[] getPackageCodePaths(Rollback rollback, String packageName) {
- File targetDir = new File(rollback.backupDir, packageName);
+ File targetDir = new File(rollback.getBackupDir(), packageName);
File[] files = targetDir.listFiles();
if (files == null || files.length == 0) {
return null;
@@ -243,7 +242,7 @@ class RollbackStore {
*/
static void deletePackageCodePaths(Rollback rollback) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
- File targetDir = new File(rollback.backupDir, info.getPackageName());
+ File targetDir = new File(rollback.getBackupDir(), info.getPackageName());
removeFile(targetDir);
}
}
@@ -255,13 +254,13 @@ class RollbackStore {
try {
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
- dataJson.put("timestamp", rollback.timestamp.toString());
- dataJson.put("stagedSessionId", rollback.stagedSessionId);
- dataJson.put("state", rollbackStateToString(rollback.state));
- dataJson.put("apkSessionId", rollback.apkSessionId);
- dataJson.put("restoreUserDataInProgress", rollback.restoreUserDataInProgress);
+ dataJson.put("timestamp", rollback.getTimestamp().toString());
+ dataJson.put("stagedSessionId", rollback.getStagedSessionId());
+ dataJson.put("state", rollback.getStateAsString());
+ dataJson.put("apkSessionId", rollback.getApkSessionId());
+ dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
- PrintWriter pw = new PrintWriter(new File(rollback.backupDir, "rollback.json"));
+ PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
pw.println(dataJson.toString());
pw.close();
} catch (JSONException e) {
@@ -273,7 +272,7 @@ class RollbackStore {
* Removes all persistent storage associated with the given rollback.
*/
void deleteRollback(Rollback rollback) {
- removeFile(rollback.backupDir);
+ removeFile(rollback.getBackupDir());
}
/**
diff --git a/services/core/java/com/android/server/stats/IonMemoryUtil.java b/services/core/java/com/android/server/stats/IonMemoryUtil.java
new file mode 100644
index 000000000000..c9be96f08876
--- /dev/null
+++ b/services/core/java/com/android/server/stats/IonMemoryUtil.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats;
+
+import android.os.FileUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Utility methods for reading ion memory stats. */
+final class IonMemoryUtil {
+ private static final String TAG = "IonMemoryUtil";
+
+ /** Path to debugfs file for the system ion heap. */
+ private static final String DEBUG_SYSTEM_ION_HEAP_FILE = "/sys/kernel/debug/ion/heaps/system";
+
+ private static final Pattern ION_HEAP_SIZE_IN_BYTES =
+ Pattern.compile("\n\\s*total\\s*(\\d+)\\s*\n");
+ private static final Pattern PROCESS_ION_HEAP_SIZE_IN_BYTES =
+ Pattern.compile("\n\\s+\\S+\\s+(\\d+)\\s+(\\d+)");
+
+ private IonMemoryUtil() {}
+
+ /**
+ * Reads size of the system ion heap from debugfs.
+ *
+ * Returns value of the total size in bytes of the system ion heap from
+ * /sys/kernel/debug/ion/heaps/system.
+ */
+ static long readSystemIonHeapSizeFromDebugfs() {
+ return parseIonHeapSizeFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
+ }
+
+ /**
+ * Parses the ion heap size from the contents of a file under /sys/kernel/debug/ion/heaps in
+ * debugfs. The returned value is in bytes.
+ */
+ @VisibleForTesting
+ static long parseIonHeapSizeFromDebugfs(String contents) {
+ if (contents.isEmpty()) {
+ return 0;
+ }
+ final Matcher matcher = ION_HEAP_SIZE_IN_BYTES.matcher(contents);
+ try {
+ return matcher.find() ? Long.parseLong(matcher.group(1)) : 0;
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return 0;
+ }
+ }
+
+ /**
+ * Reads process allocation sizes on the system ion heap from debugfs.
+ *
+ * Returns values of allocation sizes in bytes on the system ion heap from
+ * /sys/kernel/debug/ion/heaps/system.
+ */
+ static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
+ return parseProcessIonHeapSizesFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
+ }
+
+ /**
+ * Parses per-process allocation sizes on the ion heap from the contents of a file under
+ * /sys/kernel/debug/ion/heaps in debugfs.
+ */
+ @VisibleForTesting
+ static List<IonAllocations> parseProcessIonHeapSizesFromDebugfs(String contents) {
+ if (contents.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final Matcher m = PROCESS_ION_HEAP_SIZE_IN_BYTES.matcher(contents);
+ final SparseArray<IonAllocations> entries = new SparseArray<>();
+ while (m.find()) {
+ try {
+ final int pid = Integer.parseInt(m.group(1));
+ final long sizeInBytes = Long.parseLong(m.group(2));
+ IonAllocations allocations = entries.get(pid);
+ if (allocations == null) {
+ allocations = new IonAllocations();
+ entries.put(pid, allocations);
+ }
+ allocations.pid = pid;
+ allocations.totalSizeInBytes += sizeInBytes;
+ allocations.count += 1;
+ allocations.maxSizeInBytes = Math.max(allocations.maxSizeInBytes, sizeInBytes);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ }
+ }
+
+ final List<IonAllocations> result = new ArrayList<>(entries.size());
+ for (int i = 0; i < entries.size(); i++) {
+ result.add(entries.valueAt(i));
+ }
+ return result;
+ }
+
+ private static String readFile(String path) {
+ try {
+ final File file = new File(path);
+ return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read file", e);
+ return "";
+ }
+ }
+
+ /** Summary information about process ion allocations. */
+ static final class IonAllocations {
+ /** PID these allocations belong to. */
+ public int pid;
+ /** Size of all individual allocations added together. */
+ public long totalSizeInBytes;
+ /** Number of allocations. */
+ public int count;
+ /** Size of the largest allocation. */
+ public long maxSizeInBytes;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ IonAllocations that = (IonAllocations) o;
+ return pid == that.pid && totalSizeInBytes == that.totalSizeInBytes
+ && count == that.count && maxSizeInBytes == that.maxSizeInBytes;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pid, totalSizeInBytes, count, maxSizeInBytes);
+ }
+
+ @Override
+ public String toString() {
+ return "IonAllocations{"
+ + "pid=" + pid
+ + ", totalSizeInBytes=" + totalSizeInBytes
+ + ", count=" + count
+ + ", maxSizeInBytes=" + maxSizeInBytes
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c76bbb05a359..e92abfddac8f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -27,9 +27,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
-import static com.android.server.am.MemoryStatUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
-import static com.android.server.am.MemoryStatUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -138,9 +138,9 @@ import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
-import com.android.server.am.MemoryStatUtil.IonAllocations;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.role.RoleManagerInternal;
+import com.android.server.stats.IonMemoryUtil.IonAllocations;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -1112,13 +1112,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
e.writeLong(modemInfo.getTimestamp());
e.writeLong(modemInfo.getSleepTimeMillis());
e.writeLong(modemInfo.getIdleTimeMillis());
- e.writeLong(modemInfo.getTxTimeMillis()[0]);
- e.writeLong(modemInfo.getTxTimeMillis()[1]);
- e.writeLong(modemInfo.getTxTimeMillis()[2]);
- e.writeLong(modemInfo.getTxTimeMillis()[3]);
- e.writeLong(modemInfo.getTxTimeMillis()[4]);
- e.writeLong(modemInfo.getRxTimeMillis());
- e.writeLong(modemInfo.getEnergyUsed());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis());
+ e.writeLong(modemInfo.getReceiveTimeMillis());
pulledData.add(e);
}
}
@@ -1237,15 +1236,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
LocalServices.getService(
ActivityManagerInternal.class).getMemoryStateForProcesses();
for (ProcessMemoryState managedProcess : managedProcessList) {
- final long rssHighWaterMarkInBytes =
+ final int rssHighWaterMarkInKilobytes =
readRssHighWaterMarkFromProcfs(managedProcess.pid);
- if (rssHighWaterMarkInBytes == 0) {
+ if (rssHighWaterMarkInKilobytes == 0) {
continue;
}
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(managedProcess.uid);
e.writeString(managedProcess.processName);
- e.writeLong(rssHighWaterMarkInBytes);
+ // RSS high-water mark in bytes.
+ e.writeLong((long) rssHighWaterMarkInKilobytes * 1024L);
+ e.writeInt(rssHighWaterMarkInKilobytes);
pulledData.add(e);
}
int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
@@ -1253,11 +1254,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
final int pid = pids[i];
final int uid = getUidForPid(pid);
final String processName = readCmdlineFromProcfs(pid);
- final long rssHighWaterMarkInBytes = readRssHighWaterMarkFromProcfs(pid);
+ final int rssHighWaterMarkInKilobytes = readRssHighWaterMarkFromProcfs(pid);
+ if (rssHighWaterMarkInKilobytes == 0) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeString(processName);
- e.writeLong(rssHighWaterMarkInBytes);
+ // RSS high-water mark in bytes.
+ e.writeLong((long) rssHighWaterMarkInKilobytes * 1024L);
+ e.writeInt(rssHighWaterMarkInKilobytes);
pulledData.add(e);
}
// Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index 2df7370f8296..60de10c4f32a 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -42,13 +42,13 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseLongArray;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.pm.Installer;
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
index b27d5ea30c67..f8ffb7c1c0e2 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
@@ -23,7 +23,6 @@ import android.content.ServiceConnection;
import android.media.tv.ITvRemoteProvider;
import android.media.tv.ITvRemoteServiceInput;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -49,7 +48,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
private final ComponentName mComponentName;
private final int mUserId;
private final int mUid;
- private final Handler mHandler;
/**
* State guarded by mLock.
@@ -65,15 +63,14 @@ final class TvRemoteProviderProxy implements ServiceConnection {
private boolean mRunning;
private boolean mBound;
private Connection mActiveConnection;
- private boolean mConnectionReady;
- public TvRemoteProviderProxy(Context context, ComponentName componentName, int userId,
- int uid) {
+ TvRemoteProviderProxy(Context context, ProviderMethods provider,
+ ComponentName componentName, int userId, int uid) {
mContext = context;
+ mProviderMethods = provider;
mComponentName = componentName;
mUserId = userId;
mUid = uid;
- mHandler = new Handler();
}
public void dump(PrintWriter pw, String prefix) {
@@ -82,11 +79,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
pw.println(prefix + " mRunning=" + mRunning);
pw.println(prefix + " mBound=" + mBound);
pw.println(prefix + " mActiveConnection=" + mActiveConnection);
- pw.println(prefix + " mConnectionReady=" + mConnectionReady);
- }
-
- public void setProviderSink(ProviderMethods provider) {
- mProviderMethods = provider;
}
public boolean hasComponentName(String packageName, String className) {
@@ -101,7 +93,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
mRunning = true;
- updateBinding();
+ bind();
}
}
@@ -112,31 +104,19 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
mRunning = false;
- updateBinding();
+ unbind();
}
}
public void rebindIfDisconnected() {
synchronized (mLock) {
- if (mActiveConnection == null && shouldBind()) {
+ if (mActiveConnection == null && mRunning) {
unbind();
bind();
}
}
}
- private void updateBinding() {
- if (shouldBind()) {
- bind();
- } else {
- unbind();
- }
- }
-
- private boolean shouldBind() {
- return mRunning;
- }
-
private void bind() {
if (!mBound) {
if (DEBUG) {
@@ -208,48 +188,19 @@ final class TvRemoteProviderProxy implements ServiceConnection {
disconnect();
}
-
- private void onConnectionReady(Connection connection) {
- synchronized (mLock) {
- if (DEBUG) Slog.d(TAG, "onConnectionReady");
- if (mActiveConnection == connection) {
- if (DEBUG) Slog.d(TAG, "mConnectionReady = true");
- mConnectionReady = true;
- }
- }
- }
-
- private void onConnectionDied(Connection connection) {
- if (mActiveConnection == connection) {
- if (DEBUG) Slog.d(TAG, this + ": Service connection died");
- disconnect();
- }
- }
-
private void disconnect() {
synchronized (mLock) {
if (mActiveConnection != null) {
- mConnectionReady = false;
mActiveConnection.dispose();
mActiveConnection = null;
}
}
}
- // Provider helpers
- public void inputBridgeConnected(IBinder token) {
- synchronized (mLock) {
- if (DEBUG) Slog.d(TAG, this + ": inputBridgeConnected token: " + token);
- if (mConnectionReady) {
- mActiveConnection.onInputBridgeConnected(token);
- }
- }
- }
-
- public interface ProviderMethods {
+ interface ProviderMethods {
// InputBridge
- void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers);
+ boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
+ int width, int height, int maxPointers);
void closeInputBridge(TvRemoteProviderProxy provider, IBinder token);
@@ -267,7 +218,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
void sendPointerSync(TvRemoteProviderProxy provider, IBinder token);
}
- private final class Connection implements IBinder.DeathRecipient {
+ private final class Connection {
private final ITvRemoteProvider mTvRemoteProvider;
private final RemoteServiceInputProvider mServiceInputProvider;
@@ -279,24 +230,16 @@ final class TvRemoteProviderProxy implements ServiceConnection {
public boolean register() {
if (DEBUG) Slog.d(TAG, "Connection::register()");
try {
- mTvRemoteProvider.asBinder().linkToDeath(this, 0);
mTvRemoteProvider.setRemoteServiceInputSink(mServiceInputProvider);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onConnectionReady(Connection.this);
- }
- });
return true;
} catch (RemoteException ex) {
- binderDied();
+ dispose();
+ return false;
}
- return false;
}
public void dispose() {
if (DEBUG) Slog.d(TAG, "Connection::dispose()");
- mTvRemoteProvider.asBinder().unlinkToDeath(this, 0);
mServiceInputProvider.dispose();
}
@@ -310,16 +253,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
}
- @Override
- public void binderDied() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onConnectionDied(Connection.this);
- }
- });
- }
-
void openInputBridge(final IBinder token, final String name, final int width,
final int height, final int maxPointers) {
synchronized (mLock) {
@@ -330,9 +263,9 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
- name, width, height, maxPointers);
+ if (mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
+ name, width, height, maxPointers)) {
+ onInputBridgeConnected(token);
}
} finally {
Binder.restoreCallingIdentity(idToken);
@@ -356,9 +289,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
- }
+ mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -381,9 +312,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
- }
+ mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -412,10 +341,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token,
- keyCode);
- }
+ mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token, keyCode);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -438,9 +364,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
- }
+ mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -463,10 +387,8 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
- pointerId, x, y);
- }
+ mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
+ pointerId, x, y);
} finally {
Binder.restoreCallingIdentity(idToken);
}
@@ -489,10 +411,8 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
final long idToken = Binder.clearCallingIdentity();
try {
- if (mProviderMethods != null) {
- mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
- pointerId);
- }
+ mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
+ pointerId);
} finally {
Binder.restoreCallingIdentity(idToken);
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index d27970f4882c..0d29edd02663 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -45,7 +45,7 @@ final class TvRemoteProviderWatcher {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
private final Context mContext;
- private final ProviderMethods mProvider;
+ private final TvRemoteProviderProxy.ProviderMethods mProvider;
private final Handler mHandler;
private final PackageManager mPackageManager;
private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
@@ -54,10 +54,10 @@ final class TvRemoteProviderWatcher {
private boolean mRunning;
- public TvRemoteProviderWatcher(Context context, ProviderMethods provider, Handler handler) {
+ TvRemoteProviderWatcher(Context context, TvRemoteProviderProxy.ProviderMethods provider) {
mContext = context;
mProvider = provider;
- mHandler = handler;
+ mHandler = new Handler(true);
mUserId = UserHandle.myUserId();
mPackageManager = context.getPackageManager();
mUnbundledServicePackage = context.getString(
@@ -116,12 +116,11 @@ final class TvRemoteProviderWatcher {
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
if (sourceIndex < 0) {
TvRemoteProviderProxy providerProxy =
- new TvRemoteProviderProxy(mContext,
+ new TvRemoteProviderProxy(mContext, mProvider,
new ComponentName(serviceInfo.packageName, serviceInfo.name),
mUserId, serviceInfo.applicationInfo.uid);
providerProxy.start();
mProviderProxies.add(targetIndex++, providerProxy);
- mProvider.addProvider(providerProxy);
} else if (sourceIndex >= targetIndex) {
TvRemoteProviderProxy provider = mProviderProxies.get(sourceIndex);
provider.start(); // restart the provider if needed
@@ -135,7 +134,6 @@ final class TvRemoteProviderWatcher {
if (targetIndex < mProviderProxies.size()) {
for (int i = mProviderProxies.size() - 1; i >= targetIndex; i--) {
TvRemoteProviderProxy providerProxy = mProviderProxies.get(i);
- mProvider.removeProvider(providerProxy);
mProviderProxies.remove(providerProxy);
providerProxy.stop();
}
@@ -212,10 +210,4 @@ final class TvRemoteProviderWatcher {
scanPackages();
}
};
-
- public interface ProviderMethods {
- void addProvider(TvRemoteProviderProxy providerProxy);
-
- void removeProvider(TvRemoteProviderProxy providerProxy);
- }
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java
index 4a41bf8133c3..bee6fb34a899 100644
--- a/services/core/java/com/android/server/tv/TvRemoteService.java
+++ b/services/core/java/com/android/server/tv/TvRemoteService.java
@@ -17,10 +17,8 @@
package com.android.server.tv;
import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
+import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
@@ -28,7 +26,6 @@ import com.android.server.SystemService;
import com.android.server.Watchdog;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Map;
/**
@@ -43,9 +40,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
private static final boolean DEBUG = false;
private static final boolean DEBUG_KEYS = false;
+ private final TvRemoteProviderWatcher mWatcher;
private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
- private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap();
- private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>();
/**
* State guarded by mLock.
@@ -59,11 +55,10 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
*/
private final Object mLock = new Object();
- public final UserHandler mHandler;
-
public TvRemoteService(Context context) {
super(context);
- mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context);
+ mWatcher = new TvRemoteProviderWatcher(context,
+ new UserProvider(TvRemoteService.this));
Watchdog.getInstance().addMonitor(this);
}
@@ -79,21 +74,17 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
@Override
public void onBootPhase(int phase) {
+ // All lifecycle methods are called from the system server's main looper thread.
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START");
- mHandler.sendEmptyMessage(UserHandler.MSG_START);
- }
- }
- //Outgoing calls.
- private void informInputBridgeConnected(IBinder token) {
- mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget();
+ mWatcher.start(); // Also schedules the start of all providers.
+ }
}
- // Incoming calls.
- private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, IBinder token,
- String name, int width, int height,
- int maxPointers) {
+ private boolean openInputBridgeInternalLocked(final IBinder token,
+ String name, int width, int height,
+ int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
@@ -103,22 +94,31 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
//Create a new bridge, if one does not exist already
if (mBridgeMap.containsKey(token)) {
if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
- // Respond back with success.
- informInputBridgeConnected(token);
- return;
+ return true;
}
UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
-
mBridgeMap.put(token, inputBridge);
- mProviderMap.put(token, provider);
-
- // Respond back with success.
- informInputBridgeConnected(token);
+ try {
+ token.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ closeInputBridgeInternalLocked(token);
+ }
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.d(TAG, "Token is already dead");
+ closeInputBridgeInternalLocked(token);
+ return false;
+ }
} catch (IOException ioe) {
Slog.e(TAG, "Cannot create device for " + name);
+ return false;
}
+ return true;
}
private void closeInputBridgeInternalLocked(IBinder token) {
@@ -135,7 +135,6 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
mBridgeMap.remove(token);
}
-
private void clearInputBridgeInternalLocked(IBinder token) {
if (DEBUG) {
Slog.d(TAG, "clearInputBridgeInternalLocked(), token: " + token);
@@ -204,47 +203,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
}
- private final class UserHandler extends Handler {
-
- public static final int MSG_START = 1;
- public static final int MSG_INPUT_BRIDGE_CONNECTED = 2;
-
- private final TvRemoteProviderWatcher mWatcher;
- private boolean mRunning;
-
- public UserHandler(UserProvider provider, Context context) {
- super(Looper.getMainLooper(), null, true);
- mWatcher = new TvRemoteProviderWatcher(context, provider, this);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START: {
- start();
- break;
- }
- case MSG_INPUT_BRIDGE_CONNECTED: {
- IBinder token = (IBinder) msg.obj;
- TvRemoteProviderProxy provider = mProviderMap.get(token);
- if (provider != null) {
- provider.inputBridgeConnected(token);
- }
- break;
- }
- }
- }
-
- private void start() {
- if (!mRunning) {
- mRunning = true;
- mWatcher.start(); // also starts all providers
- }
- }
- }
-
- private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods,
- TvRemoteProviderProxy.ProviderMethods {
+ private final class UserProvider implements TvRemoteProviderProxy.ProviderMethods {
private final TvRemoteService mService;
@@ -253,8 +212,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
@Override
- public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers) {
+ public boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
+ int width, int height, int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridge(), token: " + token +
", name: " + name + ", width: " + width +
@@ -262,10 +221,8 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
- mService.openInputBridgeInternalLocked(provider, token, name, width, height,
- maxPointers);
- }
+ return mService.openInputBridgeInternalLocked(token, name, width,
+ height, maxPointers);
}
}
@@ -273,10 +230,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.closeInputBridgeInternalLocked(token);
- mProviderMap.remove(token);
- }
}
}
@@ -284,9 +238,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.clearInputBridgeInternalLocked(token);
- }
}
}
@@ -296,9 +248,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendKeyDownInternalLocked(token, keyCode);
- }
}
}
@@ -308,9 +258,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendKeyUpInternalLocked(token, keyCode);
- }
}
}
@@ -321,9 +269,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerDownInternalLocked(token, pointerId, x, y);
- }
}
}
@@ -333,9 +279,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerUpInternalLocked(token, pointerId);
- }
}
}
@@ -343,29 +287,7 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
synchronized (mLock) {
- if (mProviderList.contains(provider)) {
mService.sendPointerSyncInternalLocked(token);
- }
- }
- }
-
- @Override
- public void addProvider(TvRemoteProviderProxy provider) {
- if (DEBUG) Slog.d(TAG, "addProvider " + provider);
- synchronized (mLock) {
- provider.setProviderSink(this);
- mProviderList.add(provider);
- Slog.d(TAG, "provider: " + provider.toString());
- }
- }
-
- @Override
- public void removeProvider(TvRemoteProviderProxy provider) {
- if (DEBUG) Slog.d(TAG, "removeProvider " + provider);
- synchronized (mLock) {
- if (mProviderList.remove(provider) == false) {
- Slog.e(TAG, "Unknown provider " + provider);
- }
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0f1e5d69be4..3cdb59beb23c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -37,6 +37,7 @@ import android.app.UserSwitchObserver;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.app.WallpaperManager.SetWallpaperFlags;
import android.app.admin.DevicePolicyManager;
import android.app.backup.WallpaperBackupHelper;
import android.content.BroadcastReceiver;
@@ -73,7 +74,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -91,9 +91,9 @@ import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.IWindowManager;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
@@ -739,7 +739,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private final Context mContext;
- private final IWindowManager mIWindowManager;
private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager;
private final MyPackageMonitor mMonitor;
@@ -792,7 +791,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*/
private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
mColorsChangedListeners;
- private WallpaperData mLastWallpaper;
+ protected WallpaperData mLastWallpaper;
private IWallpaperManagerCallback mKeyguardListener;
private boolean mWaitingForUnlock;
private boolean mShuttingDown;
@@ -825,7 +824,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
- private WallpaperData mFallbackWallpaper;
+ protected WallpaperData mFallbackWallpaper;
private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -900,9 +899,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- WallpaperData(int userId, String inputFileName, String cropFileName) {
+ WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) {
this.userId = userId;
- final File wallpaperDir = getWallpaperDir(userId);
wallpaperFile = new File(wallpaperDir, inputFileName);
cropFile = new File(wallpaperDir, cropFileName);
}
@@ -917,7 +915,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private static final class DisplayData {
+ @VisibleForTesting
+ static final class DisplayData {
int mWidth = -1;
int mHeight = -1;
final Rect mPadding = new Rect(0, 0, 0, 0);
@@ -1057,13 +1056,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
- try {
- mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
- return;
- }
-
+ mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
@@ -1081,10 +1074,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
void disconnectLocked() {
if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
- try {
- mIWindowManager.removeWindowToken(mToken, mDisplayId);
- } catch (RemoteException e) {
- }
+ mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
+ mDisplayId);
try {
if (mEngine != null) {
mEngine.destroy();
@@ -1562,6 +1553,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ @VisibleForTesting
+ WallpaperData getCurrentWallpaperData(@SetWallpaperFlags int which, int userId) {
+ synchronized (mLock) {
+ final SparseArray<WallpaperData> wallpaperDataMap =
+ which == FLAG_SYSTEM ? mWallpaperMap : mLockWallpaperMap;
+ return wallpaperDataMap.get(userId);
+ }
+ }
+
public WallpaperManagerService(Context context) {
if (DEBUG) Slog.v(TAG, "WallpaperService startup");
mContext = context;
@@ -1569,8 +1569,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
- mIWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService(Context.WINDOW_SERVICE));
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mIPackageManager = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1600,7 +1598,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
}
- private static File getWallpaperDir(int userId) {
+ File getWallpaperDir(int userId) {
return Environment.getUserSystemDirectory(userId);
}
@@ -1819,7 +1817,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// while locked, so pretend like the component was actually
// bound into place
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
- final WallpaperData fallback = new WallpaperData(wallpaper.userId,
+ final WallpaperData fallback =
+ new WallpaperData(wallpaper.userId, getWallpaperDir(wallpaper.userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
@@ -2380,7 +2379,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// We know a-priori that there is no lock-only wallpaper currently
- WallpaperData lockWP = new WallpaperData(userId,
+ WallpaperData lockWP = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
lockWP.wallpaperId = sysWP.wallpaperId;
lockWP.cropHint.set(sysWP.cropHint);
@@ -2793,7 +2792,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private static JournaledFile makeJournaledFile(int userId) {
+ private JournaledFile makeJournaledFile(int userId) {
final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -2958,7 +2957,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// it now.
if (wallpaper == null) {
if (which == FLAG_LOCK) {
- wallpaper = new WallpaperData(userId,
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
@@ -2966,7 +2965,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// sanity fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
- wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
+ WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
}
@@ -2985,7 +2985,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Do this once per boot
migrateFromOld();
- wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
+ wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
+ WALLPAPER, WALLPAPER_CROP);
wallpaper.allowBackup = true;
mWallpaperMap.put(userId, wallpaper);
if (!wallpaper.cropExists()) {
@@ -3037,7 +3038,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// keyguard-specific wallpaper for this user
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper == null) {
- lockWallpaper = new WallpaperData(userId,
+ lockWallpaper = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, lockWallpaper);
}
@@ -3088,8 +3089,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private void initializeFallbackWallpaper() {
if (mFallbackWallpaper == null) {
if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
- mFallbackWallpaper = new WallpaperData(
- UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+ final int systemUserId = UserHandle.USER_SYSTEM;
+ mFallbackWallpaper = new WallpaperData(systemUserId, getWallpaperDir(systemUserId),
+ WALLPAPER, WALLPAPER_CROP);
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ad340fed0339..5f98d1d54a24 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1246,6 +1246,11 @@ final class AccessibilityController {
private boolean windowMattersToAccessibility(WindowState windowState,
Region regionInScreen, Region unaccountedSpace,
HashSet<Integer> skipRemainingWindowsForTasks) {
+ final RecentsAnimationController controller = mService.getRecentsAnimationController();
+ if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
+ return false;
+ }
+
if (windowState.isFocused()) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f3e7384fa9ce..e488cc91f9ba 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -740,7 +740,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
private void onSplitScreenModeDismissed() {
- mRootActivityContainer.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
// Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -764,12 +764,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
mHomeStack.moveToFront("onSplitScreenModeDismissed");
topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
}
- mRootActivityContainer.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
private void onSplitScreenModeActivated() {
- mRootActivityContainer.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
// Adjust the windowing mode of any affected by split-screen to split-screen secondary.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -784,7 +784,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
false /* creating */);
}
} finally {
- mRootActivityContainer.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
@@ -1002,12 +1002,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
Configuration values = new Configuration();
mDisplayContent.computeScreenConfiguration(values);
- if (mService.mWindowManager != null) {
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::updateOomLevelsForDisplay, mService.mAmInternal,
- mDisplayId);
- mService.mH.sendMessage(msg);
- }
+ mService.mH.sendMessage(PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateOomLevelsForDisplay, mService.mAmInternal,
+ mDisplayId));
Settings.System.clearConfiguration(values);
updateDisplayOverrideConfigurationLocked(values, null /* starting */,
@@ -1026,9 +1023,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
int changes = 0;
boolean kept = true;
- if (mService.mWindowManager != null) {
- mService.mWindowManager.deferSurfaceLayout();
- }
+ mService.deferWindowLayout();
try {
if (values != null) {
if (mDisplayId == DEFAULT_DISPLAY) {
@@ -1045,9 +1040,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
kept = mService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
} finally {
- if (mService.mWindowManager != null) {
- mService.mWindowManager.continueSurfaceLayout();
- }
+ mService.continueWindowLayout();
}
if (result != null) {
@@ -1096,6 +1089,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
mService.mWindowManager.setNewDisplayOverrideConfiguration(
overrideConfiguration, mDisplayContent);
}
+ mService.addWindowLayoutReasons(
+ ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 12cb94db0a8e..7a667315c6c3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1703,7 +1703,7 @@ final class ActivityRecord extends ConfigurationContainer {
// is not visible if it only contains finishing activities.
&& mRootActivityContainer.isTopDisplayFocusedStack(stack);
- mAtmService.mWindowManager.deferSurfaceLayout();
+ mAtmService.deferWindowLayout();
try {
makeFinishingLocked();
final TaskRecord task = getTaskRecord();
@@ -1809,7 +1809,7 @@ final class ActivityRecord extends ConfigurationContainer {
return FINISH_RESULT_REQUESTED;
} finally {
- mAtmService.mWindowManager.continueSurfaceLayout();
+ mAtmService.continueWindowLayout();
}
}
@@ -1853,9 +1853,14 @@ final class ActivityRecord extends ConfigurationContainer {
final ActivityRecord next = getDisplay().topRunningActivity(
true /* considerKeyguardState */);
final boolean isVisible = visible || nowVisible;
+ // isNextNotYetVisible is to check if the next activity is invisible, or it has been
+ // requested to be invisible but its windows haven't reported as invisible. If so, it
+ // implied that the current finishing activity should be added into stopping list rather
+ // than destroy immediately.
+ final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
final ActivityStack stack = getActivityStack();
final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
- if (isVisible && next != null && !next.nowVisible) {
+ if (isVisible && isNextNotYetVisible) {
addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
"completeFinishing");
if (DEBUG_STATES) {
@@ -2542,6 +2547,8 @@ final class ActivityRecord extends ConfigurationContainer {
return;
}
mAppWindowToken.setVisibility(visible, mDeferHidingClient);
+ mAtmService.addWindowLayoutReasons(
+ ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index b92625fe14af..50200a7cf7bc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -757,7 +757,6 @@ class ActivityStack extends ConfigurationContainer {
return;
}
- final WindowManagerService wm = mService.mWindowManager;
final ActivityRecord topActivity = getTopActivity();
// For now, assume that the Stack's windowing mode is what will actually be used
@@ -779,7 +778,7 @@ class ActivityStack extends ConfigurationContainer {
topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
}
- wm.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
if (!animate && topActivity != null) {
mStackSupervisor.mNoAnimActivities.add(topActivity);
@@ -850,7 +849,7 @@ class ActivityStack extends ConfigurationContainer {
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps();
}
- wm.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
if (!deferEnsuringVisibility) {
@@ -1750,11 +1749,11 @@ class ActivityStack extends ConfigurationContainer {
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
- mService.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
- mService.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
return;
} else {
@@ -4360,7 +4359,7 @@ class ActivityStack extends ConfigurationContainer {
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
// Update override configurations of all tasks in the stack.
final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
@@ -4384,7 +4383,7 @@ class ActivityStack extends ConfigurationContainer {
topRunningActivityLocked(), preserveWindows);
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -4516,7 +4515,7 @@ class ActivityStack extends ConfigurationContainer {
*/
void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed,
- boolean crossUser) {
+ boolean crossUser, ArraySet<Integer> profileIds) {
boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
boolean topTask = true;
int userId = UserHandle.getUserId(callingUid);
@@ -4527,8 +4526,9 @@ class ActivityStack extends ConfigurationContainer {
continue;
}
if (task.effectiveUid != callingUid) {
- if (task.userId != userId && !crossUser) {
- // Skip if the caller does not have cross user permission
+ if (task.userId != userId && !crossUser && !profileIds.contains(task.userId)) {
+ // Skip if the caller does not have cross user permission or cannot access
+ // the task's profile
continue;
}
if (!allowed && !task.isActivityTypeHome()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 7a3f022d60bf..1aa1d483f707 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1495,7 +1495,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
int toDisplayId, boolean onTop) {
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
final int windowingMode = fromStack.getWindowingMode();
final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
@@ -1561,7 +1561,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mRootActivityContainer.resumeFocusedStacksTopActivities();
} finally {
mAllowDockedStackResize = true;
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
@@ -1630,7 +1630,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
// Don't allow re-entry while resizing. E.g. due to docked stack detaching.
mAllowDockedStackResize = false;
@@ -1694,7 +1694,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
} finally {
mAllowDockedStackResize = true;
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -1718,9 +1718,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
- ActivityRecord r = stack.topRunningActivityLocked();
Rect insetBounds = null;
if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
// Use 0,0 as the position for the inset rect because we are headed for fullscreen.
@@ -1739,7 +1738,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds, !PRESERVE_WINDOWS,
!DEFER_RESUME);
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -2731,7 +2730,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
+ taskId + " can't be launch in the home/recents stack.");
}
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mWindowManager.setDockedStackCreateStateLocked(
@@ -2822,7 +2821,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
}
}
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 641b00aeb70b..b35bd9e4e81a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1400,7 +1400,7 @@ class ActivityStarter {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
- mService.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
} finally {
@@ -1436,7 +1436,7 @@ class ActivityStarter {
startedActivityStack.remove();
}
}
- mService.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
postStartActivityProcessing(r, result, startedActivityStack);
@@ -1535,6 +1535,11 @@ class ActivityStarter {
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTaskRecord() : null;
setNewTask(taskToAffiliate);
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mStartActivity.getTaskRecord())) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+ return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
} else if (mAddingToTask) {
addOrReparentStartingActivity(targetTask, "adding to task");
}
@@ -1654,9 +1659,8 @@ class ActivityStarter {
final boolean isNewClearTask =
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- if (mService.getLockTaskController().isInLockTaskMode() && (newTask
- || mService.getLockTaskController().isLockTaskModeViolation(targetTask,
- isNewClearTask))) {
+ if (!newTask && mService.getLockTaskController().isLockTaskModeViolation(targetTask,
+ isNewClearTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2538,7 +2542,8 @@ class ActivityStarter {
final boolean onTop =
(aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
final ActivityStack stack =
- mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+ mRequest.realCallingPid, mRequest.realCallingUid);
return stack;
}
// Otherwise handle adjacent launch.
@@ -2656,11 +2661,24 @@ class ActivityStarter {
return this;
}
+ /**
+ * Sets the pid of the caller who originally started the activity.
+ *
+ * Normally, the pid/uid would be the calling pid from the binder call.
+ * However, in case of a {@link PendingIntent}, the pid/uid pair of the caller is considered
+ * the original entity that created the pending intent, in contrast to setRealCallingPid/Uid,
+ * which represents the entity who invoked pending intent via {@link PendingIntent#send}.
+ */
ActivityStarter setCallingPid(int pid) {
mRequest.callingPid = pid;
return this;
}
+ /**
+ * Sets the uid of the caller who originally started the activity.
+ *
+ * @see #setCallingPid
+ */
ActivityStarter setCallingUid(int uid) {
mRequest.callingUid = uid;
return this;
@@ -2671,11 +2689,25 @@ class ActivityStarter {
return this;
}
+ /**
+ * Sets the pid of the caller who requested to launch the activity.
+ *
+ * The pid/uid represents the caller who launches the activity in this request.
+ * It will almost same as setCallingPid/Uid except when processing {@link PendingIntent}:
+ * the pid/uid will be the caller who called {@link PendingIntent#send()}.
+ *
+ * @see #setCallingPid
+ */
ActivityStarter setRealCallingPid(int pid) {
mRequest.realCallingPid = pid;
return this;
}
+ /**
+ * Sets the uid of the caller who requested to launch the activity.
+ *
+ * @see #setRealCallingPid
+ */
ActivityStarter setRealCallingUid(int uid) {
mRequest.realCallingUid = uid;
return this;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d97f0f5041a4..2f7acba595c2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -124,6 +124,7 @@ import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -215,6 +216,7 @@ import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -597,6 +599,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ LAYOUT_REASON_CONFIG_CHANGED,
+ LAYOUT_REASON_VISIBILITY_CHANGED,
+ })
+ @interface LayoutReason {}
+ static final int LAYOUT_REASON_CONFIG_CHANGED = 0x1;
+ static final int LAYOUT_REASON_VISIBILITY_CHANGED = 0x2;
+
+ /** The reasons to perform surface placement. */
+ @LayoutReason
+ private int mLayoutReasons;
+
// Whether we should show our dialogs (ANR, crash, etc) or just perform their default action
// automatically. Important for devices without direct input devices.
private boolean mShowDialogs = true;
@@ -2512,6 +2527,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final boolean crossUser = isCrossUserAllowed(callingPid, callingUid);
+ final int[] profileIds = getUserManager().getProfileIds(
+ UserHandle.getUserId(callingUid), true);
+ ArraySet<Integer> callingProfileIds = new ArraySet<>();
+ for (int i = 0; i < profileIds.length; i++) {
+ callingProfileIds.add(profileIds[i]);
+ }
ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>();
synchronized (mGlobalLock) {
@@ -2519,7 +2540,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
- ignoreWindowingMode, callingUid, allowed, crossUser);
+ ignoreWindowingMode, callingUid, allowed, crossUser, callingProfileIds);
}
return list;
@@ -4381,17 +4402,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
synchronized (mGlobalLock) {
- if (values == null && mWindowManager != null) {
+ if (mWindowManager == null) {
+ Slog.w(TAG, "Skip updateConfiguration because mWindowManager isn't set");
+ return false;
+ }
+
+ if (values == null) {
// sentinel: fetch the current configuration from the window manager
values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
}
- if (mWindowManager != null) {
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
- DEFAULT_DISPLAY);
- mH.sendMessage(msg);
- }
+ mH.sendMessage(PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
+ DEFAULT_DISPLAY));
final long origId = Binder.clearCallingIdentity();
try {
@@ -5092,9 +5115,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
int changes = 0;
boolean kept = true;
- if (mWindowManager != null) {
- mWindowManager.deferSurfaceLayout();
- }
+ deferWindowLayout();
try {
if (values != null) {
changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
@@ -5103,9 +5124,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
} finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
+ continueWindowLayout();
}
if (result != null) {
@@ -5233,6 +5252,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return changes;
}
+ /** @see WindowSurfacePlacer#deferLayout */
+ void deferWindowLayout() {
+ if (!mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ // Reset the reasons at the first entrance because we only care about the changes in the
+ // deferred scope.
+ mLayoutReasons = 0;
+ }
+
+ mWindowManager.mWindowPlacerLocked.deferLayout();
+ }
+
+ /** @see WindowSurfacePlacer#continueLayout */
+ void continueWindowLayout() {
+ mWindowManager.mWindowPlacerLocked.continueLayout(mLayoutReasons != 0);
+ if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons);
+ }
+ }
+
+ /**
+ * If a reason is added between {@link #deferWindowLayout} and {@link #continueWindowLayout},
+ * it will make sure {@link WindowSurfacePlacer#performSurfacePlacement} is called when the last
+ * defer count is gone.
+ */
+ void addWindowLayoutReasons(@LayoutReason int reasons) {
+ mLayoutReasons |= reasons;
+ }
+
private void updateEventDispatchingLocked(boolean booted) {
mWindowManager.setEventDispatching(booted && !mShuttingDown);
}
@@ -6660,7 +6707,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
if (!restarting && hasVisibleActivities) {
- mWindowManager.deferSurfaceLayout();
+ deferWindowLayout();
try {
if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
// If there was nothing to resume, and we are not already restarting
@@ -6671,7 +6718,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
!PRESERVE_WINDOWS);
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ continueWindowLayout();
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5ec167e495e0..f2ad56a8fdfa 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -100,6 +100,7 @@ import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -942,14 +943,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer();
- mWindowingLayer = b.setName("Display Root").build();
- mOverlayLayer = b.setName("Display Overlays").build();
-
- getPendingTransaction().setLayer(mWindowingLayer, 0)
- .setLayerStack(mWindowingLayer, mDisplayId)
+ mSurfaceControl = b.setName("Root").setContainerLayer().build();
+ mWindowingLayer = b.setName("Display Windows").setParent(mSurfaceControl).build();
+ mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
+
+ getPendingTransaction()
+ .setLayer(mSurfaceControl, 0)
+ .setLayerStack(mSurfaceControl, mDisplayId)
+ .show(mSurfaceControl)
+ .setLayer(mWindowingLayer, 0)
.show(mWindowingLayer)
.setLayer(mOverlayLayer, 1)
- .setLayerStack(mOverlayLayer, mDisplayId)
.show(mOverlayLayer);
getPendingTransaction().apply();
@@ -1056,9 +1060,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// removing from parent.
token.getParent().removeChild(token);
}
- if (prevDc.mLastFocus == mCurrentFocus) {
- // The window has become the focus of this display, so it should not be notified
- // that it lost focus from the previous display.
+ if (token.hasChild(prevDc.mLastFocus)) {
+ // If the reparent window token contains previous display's last focus window, means
+ // it will end up to gain window focus on the target display, so it should not be
+ // notified that it lost focus from the previous display.
prevDc.mLastFocus = null;
}
}
@@ -2916,9 +2921,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
}
- if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
- + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
- + " Callers=" + Debug.getCallers(4));
+ if (DEBUG_FOCUS_LIGHT || DEBUG) {
+ Slog.v(TAG_WM, "Changing focus from "
+ + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+ + " Callers=" + Debug.getCallers(4));
+ }
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
@@ -4879,7 +4886,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mPortalWindowHandle = createPortalWindowHandle(sc.toString());
}
getPendingTransaction().setInputWindowInfo(sc, mPortalWindowHandle)
- .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
+ .reparent(mSurfaceControl, sc);
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b502bd54bfc5..4dbb0092140c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -103,7 +103,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.localLOGV;
import android.Manifest.permission;
import android.annotation.NonNull;
@@ -2597,7 +2596,7 @@ public class DisplayPolicy {
}
final int fl = PolicyControl.getWindowFlags(null,
mTopFullscreenOpaqueWindowState.getAttrs());
- if (localLOGV) {
+ if (WindowManagerDebugConfig.DEBUG) {
Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(fl));
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1b7b92bca250..34253ed6fc8c 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -51,8 +51,11 @@ class InsetsSourceProvider {
private final @NonNull InsetsSource mSource;
private final DisplayContent mDisplayContent;
private final InsetsStateController mStateController;
+ private final InsetsSourceControl mFakeControl;
private @Nullable InsetsSourceControl mControl;
private @Nullable InsetsControlTarget mControlTarget;
+ private @Nullable InsetsControlTarget mFakeControlTarget;
+
private @Nullable ControlAdapter mAdapter;
private WindowState mWin;
private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
@@ -73,6 +76,8 @@ class InsetsSourceProvider {
mSource = source;
mDisplayContent = displayContent;
mStateController = stateController;
+ mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */,
+ new Point());
final int type = source.getType();
if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
@@ -150,6 +155,16 @@ class InsetsSourceProvider {
&& !mWin.mGivenInsetsPending);
}
+ /**
+ * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
+ */
+ void updateControlForFakeTarget(@Nullable InsetsControlTarget fakeTarget) {
+ if (fakeTarget == mFakeControlTarget) {
+ return;
+ }
+ mFakeControlTarget = fakeTarget;
+ }
+
void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
if (mWin == null) {
mControlTarget = target;
@@ -199,8 +214,14 @@ class InsetsSourceProvider {
mSource.setVisible(mServerVisible && mClientVisible);
}
- InsetsSourceControl getControl() {
- return mControl;
+ InsetsSourceControl getControl(InsetsControlTarget target) {
+ if (target == mControlTarget) {
+ return mControl;
+ }
+ if (target == mFakeControlTarget) {
+ return mFakeControl;
+ }
+ return null;
}
boolean isClientVisible() {
@@ -257,5 +278,5 @@ class InsetsSourceProvider {
@Override
public void writeToProto(ProtoOutputStream proto) {
}
- };
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index bb704957a55a..4ebb553318e8 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -20,6 +20,8 @@ import static android.view.InsetsState.InternalInsetType;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,6 +49,10 @@ class InsetsStateController {
private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
new ArrayMap<>();
private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
+
+ /** @see #onControlFakeTargetChanged */
+ private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>();
+
private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
@@ -93,7 +99,7 @@ class InsetsStateController {
final int size = controlled.size();
final InsetsSourceControl[] result = new InsetsSourceControl[size];
for (int i = 0; i < size; i++) {
- result[i] = mProviders.get(controlled.get(i)).getControl();
+ result[i] = mProviders.get(controlled.get(i)).getControl(target);
}
return result;
}
@@ -157,7 +163,8 @@ class InsetsStateController {
void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
InsetsSourceProvider provider) {
- removeFromControlMaps(previousControlTarget, provider.getSource().getType());
+ removeFromControlMaps(previousControlTarget, provider.getSource().getType(),
+ false /* fake */);
}
private void onControlChanged(@InternalInsetType int type,
@@ -175,17 +182,47 @@ class InsetsStateController {
}
provider.updateControlForTarget(target, false /* force */);
if (previous != null) {
- removeFromControlMaps(previous, type);
+ removeFromControlMaps(previous, type, false /* fake */);
mPendingControlChanged.add(previous);
}
if (target != null) {
- addToControlMaps(target, type);
+ addToControlMaps(target, type, false /* fake */);
mPendingControlChanged.add(target);
}
}
+ /**
+ * The fake target saved here will be used to pretend to the app that it's still under control
+ * of the bars while it's not really, but we still need to find out the apps intentions around
+ * showing/hiding. For example, when the transient bars are showing, and the fake target
+ * requests to show system bars, the transient state will be aborted.
+ */
+ void onControlFakeTargetChanged(@InternalInsetType int type,
+ @Nullable InsetsControlTarget fakeTarget) {
+ if (sNewInsetsMode != NEW_INSETS_MODE_FULL) {
+ return;
+ }
+ final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type);
+ if (fakeTarget == previous) {
+ return;
+ }
+ final InsetsSourceProvider provider = mProviders.get(type);
+ if (provider == null) {
+ return;
+ }
+ provider.updateControlForFakeTarget(fakeTarget);
+ if (previous != null) {
+ removeFromControlMaps(previous, type, true /* fake */);
+ mPendingControlChanged.add(previous);
+ }
+ if (fakeTarget != null) {
+ addToControlMaps(fakeTarget, type, true /* fake */);
+ mPendingControlChanged.add(fakeTarget);
+ }
+ }
+
private void removeFromControlMaps(@NonNull InsetsControlTarget target,
- @InternalInsetType int type) {
+ @InternalInsetType int type, boolean fake) {
final ArrayList<Integer> array = mControlTargetTypeMap.get(target);
if (array == null) {
return;
@@ -194,15 +231,23 @@ class InsetsStateController {
if (array.isEmpty()) {
mControlTargetTypeMap.remove(target);
}
- mTypeControlTargetMap.remove(type);
+ if (fake) {
+ mTypeFakeControlTargetMap.remove(type);
+ } else {
+ mTypeControlTargetMap.remove(type);
+ }
}
private void addToControlMaps(@NonNull InsetsControlTarget target,
- @InternalInsetType int type) {
+ @InternalInsetType int type, boolean fake) {
final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target,
key -> new ArrayList<>());
array.add(type);
- mTypeControlTargetMap.put(type, target);
+ if (fake) {
+ mTypeFakeControlTargetMap.put(type, target);
+ } else {
+ mTypeControlTargetMap.put(type, target);
+ }
}
void notifyControlChanged(InsetsControlTarget target) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 422b6e58e0ed..d528ef6ec6a5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -182,7 +182,7 @@ class KeyguardController {
return;
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
setKeyguardGoingAway(true);
EventLog.writeEvent(EventLogTags.AM_SET_KEYGUARD_SHOWN,
@@ -204,7 +204,7 @@ class KeyguardController {
mWindowManager.executeAppTransition();
} finally {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -330,7 +330,7 @@ class KeyguardController {
mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
if (isKeyguardLocked()) {
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
mRootActivityContainer.getDefaultDisplay().mDisplayContent
.prepareAppTransition(resolveOccludeTransit(),
@@ -340,7 +340,7 @@ class KeyguardController {
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
dismissDockedStackIfNeeded();
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 59df09be1ff5..6de48d1b2d07 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -133,7 +133,7 @@ class LaunchParamsController {
return false;
}
- mService.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
if (mTmpParams.hasPreferredDisplay()
@@ -161,7 +161,7 @@ class LaunchParamsController {
task.setLastNonFullscreenBounds(mTmpParams.mBounds);
return false;
} finally {
- mService.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index caf87cd6a906..b30da5e156e2 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -280,13 +280,6 @@ public class LockTaskController {
}
/**
- * @return true if currently in the lock task mode, otherwise, return false.
- */
- boolean isInLockTaskMode() {
- return !mLockTaskModeTasks.isEmpty();
- }
-
- /**
* @return whether the requested task is disallowed to be launched.
*/
boolean isLockTaskModeViolation(TaskRecord task) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 1a8944ab415b..5cabbd97b1f7 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -196,7 +196,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mCaller.setRunningRecentsAnimation(true);
}
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
@@ -260,7 +260,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
Slog.e(TAG, "Failed to start recents activity", e);
throw e;
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -297,7 +297,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mWindowManager.inSurfaceTransaction(() -> {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
"RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
mWindowManager.cleanupRecentsAnimation(reorderMode);
@@ -387,7 +387,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
Slog.e(TAG, "Failed to clean up recents activity", e);
throw e;
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
});
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index f4280a2974e0..19b5f3160837 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -760,6 +760,11 @@ public class RecentsAnimationController implements DeathRecipient {
return false;
}
+ boolean shouldIgnoreForAccessibility(WindowState windowState) {
+ final Task task = windowState.getTask();
+ return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mAppToken);
+ }
+
@VisibleForTesting
class TaskAnimationAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 50b5902e17d4..4b2d4ce3d799 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -945,8 +945,7 @@ class RootActivityContainer extends ConfigurationContainer
void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
String reason) {
-
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
final ActivityDisplay display = r.getActivityStack().getDisplay();
ActivityStack stack = display.getPinnedStack();
@@ -999,7 +998,7 @@ class RootActivityContainer extends ConfigurationContainer
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
@@ -1615,7 +1614,8 @@ class RootActivityContainer extends ConfigurationContainer
<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
- return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+ return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */,
+ -1 /* no realCallingPid */, -1 /* no realCallingUid */);
}
/**
@@ -1624,13 +1624,16 @@ class RootActivityContainer extends ConfigurationContainer
* @param r The activity we are trying to launch. Can be null.
* @param options The activity options used to the launch. Can be null.
* @param candidateTask The possible task the activity might be launched in. Can be null.
- * @params launchParams The resolved launch params to use.
+ * @param launchParams The resolved launch params to use.
+ * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
+ * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
*
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
+ @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid,
+ int realCallingUid) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
//Rect bounds = null;
@@ -1661,7 +1664,15 @@ class RootActivityContainer extends ConfigurationContainer
if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
displayId = launchParams.mPreferredDisplayId;
}
- if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+ final boolean canLaunchOnDisplayFromStartRequest =
+ realCallingPid != 0 && realCallingUid > 0 && r != null
+ && mStackSupervisor.canPlaceEntityOnDisplay(displayId, realCallingPid,
+ realCallingUid, r.info);
+ // Checking if the activity's launch caller, or the realCallerId of the activity from
+ // start request (i.e. entity that invokes PendingIntent) is allowed to launch on the
+ // display.
+ if (displayId != INVALID_DISPLAY && (canLaunchOnDisplay(r, displayId)
+ || canLaunchOnDisplayFromStartRequest)) {
if (r != null) {
stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
launchParams);
@@ -2060,7 +2071,7 @@ class RootActivityContainer extends ConfigurationContainer
* @param userId user handle for the locked managed profile.
*/
void lockAllProfileTasks(@UserIdInt int userId) {
- mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
@@ -2081,7 +2092,7 @@ class RootActivityContainer extends ConfigurationContainer
}
}
} finally {
- mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
@@ -2214,9 +2225,9 @@ class RootActivityContainer extends ConfigurationContainer
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
@WindowConfiguration.ActivityType int ignoreActivityType,
@WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
- boolean allowed, boolean crossUser) {
+ boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
- ignoreWindowingMode, mActivityDisplays, callingUid, allowed, crossUser);
+ ignoreWindowingMode, mActivityDisplays, callingUid, allowed, crossUser, profileIds);
}
void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 22a9c32a830f..81a85476c53a 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
+import android.util.ArraySet;
import java.util.ArrayList;
import java.util.Comparator;
@@ -40,7 +41,7 @@ class RunningTasks {
void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
- int callingUid, boolean allowed, boolean crossUser) {
+ int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -55,7 +56,7 @@ class RunningTasks {
final ActivityStack stack = display.getChildAt(stackNdx);
mTmpStackTasks.clear();
stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
- callingUid, allowed, crossUser);
+ callingUid, allowed, crossUser, profileIds);
mTmpSortedSet.addAll(mTmpStackTasks);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 4a76042a2c66..72bb355146d1 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -24,6 +24,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -227,8 +228,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public void finishDrawing(IWindow window,
@Nullable SurfaceControl.Transaction postDrawTransaction) {
- if (WindowManagerService.localLOGV) Slog.v(
- TAG_WM, "IWindow finishDrawing called for " + window);
+ if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
mService.finishDrawingWindow(this, window, postDrawTransaction);
}
@@ -474,8 +474,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) {
- if (WindowManagerService.localLOGV) Slog.v(
- TAG_WM, "First window added to " + this + ", creating SurfaceSession");
+ if (DEBUG) {
+ Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
+ }
mSurfaceSession = new SurfaceSession();
if (SHOW_TRANSACTIONS) Slog.i(
TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession);
@@ -565,8 +566,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
return;
}
- if (WindowManagerService.localLOGV) Slog.v(TAG_WM, "Last window removed from " + this
- + ", destroying " + mSurfaceSession);
+ if (DEBUG) {
+ Slog.v(TAG_WM, "Last window removed from " + this
+ + ", destroying " + mSurfaceSession);
+ }
if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " KILL SURFACE SESSION " + mSurfaceSession);
try {
mSurfaceSession.kill();
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index d3f3981625d9..4b3691c88a06 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -545,7 +545,7 @@ class TaskRecord extends ConfigurationContainer {
}
boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
- mService.mWindowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
try {
if (!isResizeable()) {
@@ -619,7 +619,7 @@ class TaskRecord extends ConfigurationContainer {
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
return kept;
} finally {
- mService.mWindowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
}
@@ -725,7 +725,7 @@ class TaskRecord extends ConfigurationContainer {
windowManager.setWillReplaceWindow(topActivity.appToken, animate);
}
- windowManager.deferSurfaceLayout();
+ mService.deferWindowLayout();
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
@@ -809,7 +809,7 @@ class TaskRecord extends ConfigurationContainer {
!mightReplaceWindow, deferResume);
}
} finally {
- windowManager.continueSurfaceLayout();
+ mService.continueWindowLayout();
}
if (mightReplaceWindow) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 32a1d9061160..a156f5c240a8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -29,11 +29,11 @@ import android.graphics.Bitmap.Config;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
+import android.util.AtomicFile;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.AtomicFile;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
import java.io.File;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bef6a37a1ebe..239bd004705f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1746,6 +1746,11 @@ public class TaskStack extends WindowContainer<Task> implements
if (toBounds.width() == fromBounds.width()
&& toBounds.height() == fromBounds.height()) {
intendedAnimationType = BoundsAnimationController.BOUNDS;
+ } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
+ // intendedAnimationType may have been reset at the end of RecentsAnimation,
+ // force it to BOUNDS type if we know for certain we're animating to
+ // a different bounds, especially for expand and collapse of PiP window.
+ intendedAnimationType = BoundsAnimationController.BOUNDS;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8d3a107c2f25..607a013abc51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -297,7 +297,6 @@ public class WindowManagerService extends IWindowManager.Stub
static final int LAYOUT_REPEAT_THRESHOLD = 4;
static final boolean PROFILE_ORIENTATION = false;
- static final boolean localLOGV = DEBUG;
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
@@ -1217,7 +1216,8 @@ public class WindowManagerService extends IWindowManager.Stub
mPropertiesChangedListener = properties -> {
synchronized (mGlobalLock) {
final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
- properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
@@ -1671,8 +1671,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
- + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
+ if (DEBUG || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG_WM, "addWindow: New client " + client.asBinder()
+ + ": window=" + win + " Callers=" + Debug.getCallers(5));
+ }
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
@@ -2339,16 +2341,18 @@ public class WindowManagerService extends IWindowManager.Stub
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
- if (localLOGV) Slog.v(
- TAG_WM, "Relayout given client " + client.asBinder()
- + ", requestedWidth=" + requestedWidth
- + ", requestedHeight=" + requestedHeight
- + ", viewVisibility=" + viewVisibility
- + "\nRelayout returning frame=" + outFrame
- + ", surface=" + outSurfaceControl);
+ if (DEBUG) {
+ Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
+ + ", requestedWidth=" + requestedWidth
+ + ", requestedHeight=" + requestedHeight
+ + ", viewVisibility=" + viewVisibility
+ + "\nRelayout returning frame=" + outFrame
+ + ", surface=" + outSurfaceControl);
+ }
- if (localLOGV || DEBUG_FOCUS) Slog.v(
- TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
+ if (DEBUG || DEBUG_FOCUS) {
+ Slog.v(TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
+ }
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
@@ -2775,21 +2779,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Starts deferring layout passes. Useful when doing multiple changes but to optimize
- * performance, only one layout pass should be done. This can be called multiple times, and
- * layouting will be resumed once the last caller has called
- * {@link #continueSurfaceLayout}.
- */
- void deferSurfaceLayout() {
- mWindowPlacerLocked.deferLayout();
- }
-
- /** Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()} */
- void continueSurfaceLayout() {
- mWindowPlacerLocked.continueLayout();
- }
-
- /**
* Notifies activity manager that some Keyguard flags have changed and that it needs to
* reevaluate the visibilities of the activities.
* @param callback Runnable to be called when activity manager is done reevaluating visibilities
@@ -5242,7 +5231,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {
WindowState win = mWindowMap.get(client);
- if (localLOGV) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);
+ if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);
if (win == null) {
if (throwOnError) {
throw new IllegalArgumentException(
@@ -7760,4 +7749,64 @@ public class WindowManagerService extends IWindowManager.Stub
// InputDispatcher hold the last ref.
inputChannel.release();
}
+
+ /** Return whether layer tracing is enabled */
+ public boolean isLayerTracing() {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
+ "isLayerTracing");
+ long token = Binder.clearCallingIdentity();
+ try {
+ Parcel data = null;
+ Parcel reply = null;
+ try {
+ IBinder sf = ServiceManager.getService("SurfaceFlinger");
+ if (sf != null) {
+ reply = Parcel.obtain();
+ data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */);
+ return reply.readBoolean();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get layer tracing");
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ if (reply != null) {
+ reply.recycle();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return false;
+ }
+
+ /** Enable or disable layer tracing */
+ public void setLayerTracing(boolean enabled) {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
+ "setLayerTracing");
+ long token = Binder.clearCallingIdentity();
+ try {
+ Parcel data = null;
+ try {
+ IBinder sf = ServiceManager.getService("SurfaceFlinger");
+ if (sf != null) {
+ data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(enabled ? 1 : 0);
+ sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set layer tracing");
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cbb0b3aab687..99ae18d67be5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -90,6 +90,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
import static com.android.server.wm.MoveAnimationSpecProto.FROM;
import static com.android.server.wm.MoveAnimationSpecProto.TO;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -115,7 +116,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -755,9 +755,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
- if (localLOGV) Slog.v(
- TAG, "Window " + this + " client=" + c.asBinder()
- + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
+ if (DEBUG) {
+ Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
+ }
try {
c.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
@@ -825,7 +826,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void attach() {
- if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
+ if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked(mAttrs.packageName);
}
@@ -1125,13 +1126,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- if (DEBUG_LAYOUT || localLOGV) Slog.v(TAG,
- "Resolving (mRequestedWidth="
- + mRequestedWidth + ", mRequestedheight="
- + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + mWindowFrames.mFrame.toShortString()
- + " " + mWindowFrames.getInsetsInfo()
- + " " + mAttrs.getTitle());
+ if (DEBUG_LAYOUT || DEBUG) {
+ Slog.v(TAG, "Resolving (mRequestedWidth="
+ + mRequestedWidth + ", mRequestedheight="
+ + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ + "): frame=" + mWindowFrames.mFrame.toShortString()
+ + " " + mWindowFrames.getInsetsInfo()
+ + " " + mAttrs.getTitle());
+ }
}
// TODO: Look into whether this override is still necessary.
@@ -1280,9 +1282,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean dragResizingChanged = isDragResizeChanged()
&& !isDragResizingChangeReported();
- if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged
- + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
+ if (DEBUG) {
+ Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
+ + " dragResizingChanged=" + dragResizingChanged
+ + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
+ }
// We update mLastFrame always rather than in the conditional with the last inset
// variables, because mFrameSizeChanged only tracks the width and height changing.
@@ -1979,11 +1983,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
"Starting window removed " + this);
- if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
+ if (DEBUG || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused()) {
Slog.v(TAG_WM, "Remove " + this + " client="
- + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
- + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
- + Debug.getCallers(5));
+ + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
+ + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
+ + Debug.getCallers(5));
+ }
final long origId = Binder.clearCallingIdentity();
@@ -4377,8 +4382,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
- "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
+ if (DEBUG || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
+ }
mDestroying = true;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6dfbc36ce6f7..ef1d110c9617 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -540,8 +541,10 @@ class WindowStateAnimator {
return null;
}
- if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
- + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top);
+ if (DEBUG) {
+ Slog.v(TAG, "Got surface: " + mSurfaceController
+ + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top);
+ }
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
@@ -552,7 +555,7 @@ class WindowStateAnimator {
mLastHidden = true;
- if (WindowManagerService.localLOGV) Slog.v(TAG, "Created surface " + this);
+ if (DEBUG) Slog.v(TAG, "Created surface " + this);
return mSurfaceController;
}
@@ -745,11 +748,11 @@ class WindowStateAnimator {
mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
}
- if ((DEBUG_ANIM || WindowManagerService.localLOGV)
- && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
- TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
- + " screen=" + (screenAnimation ?
- screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
+ if ((DEBUG_ANIM || DEBUG) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) {
+ Slog.v(TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
+ + " screen=" + (screenAnimation
+ ? screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
+ }
return;
} else if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
@@ -762,9 +765,10 @@ class WindowStateAnimator {
return;
}
- if (WindowManagerService.localLOGV) Slog.v(
- TAG, "computeShownFrameLocked: " + this +
- " not attached, mAlpha=" + mAlpha);
+ if (DEBUG) {
+ Slog.v(TAG, "computeShownFrameLocked: " + this
+ + " not attached, mAlpha=" + mAlpha);
+ }
mShownAlpha = mAlpha;
mHaveMatrix = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cc7917879634..56f6d4b02e32 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -25,7 +25,6 @@ import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD
import android.os.Debug;
import android.os.Trace;
import android.util.Slog;
-import android.util.SparseIntArray;
import java.io.PrintWriter;
@@ -50,8 +49,8 @@ class WindowSurfacePlacer {
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
-
- private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
+ /** The number of layout requests when deferring. */
+ private int mDeferredRequests;
private final Runnable mPerformSurfacePlacement;
@@ -65,19 +64,38 @@ class WindowSurfacePlacer {
}
/**
- * See {@link WindowManagerService#deferSurfaceLayout()}
+ * Starts deferring layout passes. Useful when doing multiple changes but to optimize
+ * performance, only one layout pass should be done. This can be called multiple times, and
+ * layouting will be resumed once the last caller has called {@link #continueLayout}.
*/
void deferLayout() {
mDeferDepth++;
}
/**
- * See {@link WindowManagerService#continueSurfaceLayout()}
+ * Resumes layout passes after deferring them. If there is a deferred direct invocation of
+ * {@link #performSurfacePlacement} ({@link #mDeferredRequests} > 0), when the defer is
+ * done, it will continue to perform layout.
+ *
+ * @param hasChanges Something has changed. That means whether to call
+ * {@link #performSurfacePlacement} when {@link #mDeferDepth} becomes zero.
+ * @see #deferLayout
*/
- void continueLayout() {
+ void continueLayout(boolean hasChanges) {
mDeferDepth--;
- if (mDeferDepth <= 0) {
+ if (mDeferDepth > 0) {
+ return;
+ }
+
+ if (hasChanges || mDeferredRequests > 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "continueLayout hasChanges=" + hasChanges
+ + " deferredRequests=" + mDeferredRequests + " " + Debug.getCallers(2, 3));
+ }
performSurfacePlacement();
+ mDeferredRequests = 0;
+ } else if (DEBUG) {
+ Slog.i(TAG, "Cancel continueLayout " + Debug.getCallers(2, 3));
}
}
@@ -97,6 +115,7 @@ class WindowSurfacePlacer {
final void performSurfacePlacement(boolean force) {
if (mDeferDepth > 0 && !force) {
+ mDeferredRequests++;
return;
}
int loopCount = 6;
@@ -195,10 +214,19 @@ class WindowSurfacePlacer {
}
void requestTraversal() {
- if (!mTraversalScheduled) {
- mTraversalScheduled = true;
- mService.mAnimationHandler.post(mPerformSurfacePlacement);
+ if (mTraversalScheduled) {
+ return;
+ }
+
+ // Set as scheduled even the request will be deferred because mDeferredRequests is also
+ // increased, then the end of deferring will perform the request.
+ mTraversalScheduled = true;
+ if (mDeferDepth > 0) {
+ mDeferredRequests++;
+ if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
+ return;
}
+ mService.mAnimationHandler.post(mPerformSurfacePlacement);
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7274d1765329..d9c7fed0ff00 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -57,12 +57,15 @@ cc_library_static {
],
include_dirs: [
- "bionic/libc/private",
"frameworks/base/libs",
"frameworks/native/services",
"system/gatekeeper/include",
],
+ header_libs: [
+ "bionic_libc_platform_headers",
+ ],
+
product_variables: {
arc: {
exclude_srcs: [
@@ -109,8 +112,6 @@ cc_defaults {
"libGLESv2",
"libnetutils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"libutils",
"libhwui",
"libbpf_android",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 159a4960731d..78b64ca072ad 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -24,7 +24,7 @@
#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include <cutils/properties.h>
#include <utils/Log.h>
diff --git a/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
index b53ea925e837..2b1c83f773da 100644
--- a/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
+++ b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
@@ -22,7 +22,7 @@
namespace {
static jint runSelfTest(JNIEnv* env, jobject /* clazz */) {
- return BORINGSSL_self_test();
+ return FIPS_mode();
}
static const JNINativeMethod methods[] = {
@@ -39,4 +39,4 @@ int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv *env) {
env, "com/android/server/devicepolicy/CryptoTestHelper", methods, NELEM(methods));
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 478bc88fe815..3154c7021255 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1999,6 +1999,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return LocalServices.getService(LockSettingsInternal.class);
}
+ boolean hasUserSetupCompleted(DevicePolicyData userData) {
+ return userData.mUserSetupComplete;
+ }
+
boolean isBuildDebuggable() {
return Build.IS_DEBUGGABLE;
}
@@ -8271,7 +8275,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- return getUserData(userHandle).mUserSetupComplete;
+ return mInjector.hasUserSetupCompleted(getUserData(userHandle));
}
private boolean hasPaired(int userHandle) {
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index ad94e6159b87..8699669bf4a5 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -25,6 +25,7 @@ android_test {
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth-prebuilt",
+ "testables",
],
libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
new file mode 100644
index 000000000000..307092d24d84
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
+
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.WallpaperService;
+import android.testing.TestableContext;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.wallpaper.WallpaperManagerService.WallpaperData;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Tests for the {@link WallpaperManagerService} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:WallpaperManagerServiceTests
+ */
+@Presubmit
+@FlakyTest(bugId = 129797242)
+@RunWith(AndroidJUnit4.class)
+public class WallpaperManagerServiceTests {
+ private static final int DISPLAY_SIZE_DIMENSION = 100;
+ private static StaticMockitoSession sMockitoSession;
+
+ @ClassRule
+ public static final TestableContext sContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ private static ComponentName sImageWallpaperComponentName;
+ private static ComponentName sDefaultWallpaperComponent;
+
+ private IPackageManager mIpm = AppGlobals.getPackageManager();
+
+ @Mock
+ private DisplayManager mDisplayManager;
+
+ @Rule
+ public final TemporaryFolder mFolder = new TemporaryFolder();
+ private final SparseArray<File> mTempDirs = new SparseArray<>();
+ private WallpaperManagerService mService;
+
+ @BeforeClass
+ public static void setUpClass() {
+ sMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(LocalServices.class)
+ .spyStatic(WallpaperManager.class)
+ .startMocking();
+
+ final WindowManagerInternal dmi = mock(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, dmi);
+
+ sContext.addMockSystemService(Context.APP_OPS_SERVICE, mock(AppOpsManager.class));
+
+ spyOn(sContext);
+ sContext.getTestablePermissions().setPermission(
+ android.Manifest.permission.SET_WALLPAPER_COMPONENT,
+ PackageManager.PERMISSION_GRANTED);
+ sContext.getTestablePermissions().setPermission(
+ android.Manifest.permission.SET_WALLPAPER,
+ PackageManager.PERMISSION_GRANTED);
+ doNothing().when(sContext).sendBroadcastAsUser(any(), any());
+
+ //Wallpaper components
+ final IWallpaperConnection.Stub wallpaperService = mock(IWallpaperConnection.Stub.class);
+ sImageWallpaperComponentName = ComponentName.unflattenFromString(
+ sContext.getResources().getString(R.string.image_wallpaper_component));
+ // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
+ sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext);
+
+ if (sDefaultWallpaperComponent == null) {
+ sDefaultWallpaperComponent = sImageWallpaperComponentName;
+ doReturn(sImageWallpaperComponentName).when(() ->
+ WallpaperManager.getDefaultWallpaperComponent(any()));
+ } else {
+ sContext.addMockService(sDefaultWallpaperComponent, wallpaperService);
+ }
+
+ sContext.addMockService(sImageWallpaperComponentName, wallpaperService);
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ if (sMockitoSession != null) {
+ sMockitoSession.finishMocking();
+ sMockitoSession = null;
+ }
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ sImageWallpaperComponentName = null;
+ sDefaultWallpaperComponent = null;
+ reset(sContext);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ sContext.addMockSystemService(DisplayManager.class, mDisplayManager);
+
+ final Display mockDisplay = mock(Display.class);
+ doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension();
+ doReturn(mockDisplay).when(mDisplayManager).getDisplay(anyInt());
+
+ final Display[] displays = new Display[]{mockDisplay};
+ doReturn(displays).when(mDisplayManager).getDisplays();
+
+ spyOn(mIpm);
+ mService = new TestWallpaperManagerService(sContext);
+ spyOn(mService);
+ mService.systemReady();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WallpaperManagerInternal.class);
+
+ mTempDirs.clear();
+ reset(mIpm);
+ mService = null;
+ }
+
+ protected class TestWallpaperManagerService extends WallpaperManagerService {
+ private static final String TAG = "TestWallpaperManagerService";
+
+ TestWallpaperManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ File getWallpaperDir(int userId) {
+ File tempDir = mTempDirs.get(userId);
+ if (tempDir == null) {
+ try {
+ tempDir = mFolder.newFolder(String.valueOf(userId));
+ mTempDirs.append(userId, tempDir);
+ } catch (IOException e) {
+ Log.e(TAG, "getWallpaperDir failed at userId= " + userId);
+ }
+ }
+ return tempDir;
+ }
+
+ // Always return true for test
+ @Override
+ public boolean isWallpaperSupported(String callingPackage) {
+ return true;
+ }
+
+ // Always return true for test
+ @Override
+ public boolean isSetWallpaperAllowed(String callingPackage) {
+ return true;
+ }
+ }
+
+ /**
+ * Tests that internal basic data should be correct after boot up.
+ */
+ @Test
+ public void testDataCorrectAfterBoot() {
+ mService.switchUser(UserHandle.USER_SYSTEM, null);
+
+ final WallpaperData fallbackData = mService.mFallbackWallpaper;
+ assertEquals("Fallback wallpaper component should be ImageWallpaper.",
+ sImageWallpaperComponentName, fallbackData.wallpaperComponent);
+
+ verifyLastWallpaperData(UserHandle.USER_SYSTEM, sDefaultWallpaperComponent);
+ verifyDisplayData();
+ }
+
+ /**
+ * Tests setWallpaperComponent and clearWallpaper should work as expected.
+ */
+ @Test
+ public void testSetThenClearComponent() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = UserHandle.USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(testUserId);
+
+ mService.setWallpaperComponent(sImageWallpaperComponentName);
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+
+ mService.clearWallpaper(null, FLAG_SYSTEM, testUserId);
+ verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(testUserId);
+ }
+
+ /**
+ * Tests internal data should be correct and no crash after switch user continuously.
+ */
+ @Test
+ public void testSwitchMultipleUsers() throws Exception {
+ final int lastUserId = 5;
+ final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+
+ final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ final ParceledListSlice ris =
+ mIpm.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(sContext.getContentResolver()),
+ PackageManager.GET_META_DATA, 0);
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
+ eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
+
+ for (int userId = 0; userId <= lastUserId; userId++) {
+ mService.switchUser(userId, null);
+ verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(userId);
+ }
+ verifyNoConnectionBeforeLastUser(lastUserId);
+ }
+
+ /**
+ * Tests internal data should be correct and no crash after switch user + unlock user
+ * continuously.
+ * Simulating that the selected WallpaperService is not built-in. After switching users, the
+ * service should not be bound, but bound to the image wallpaper. After receiving the user
+ * unlock callback and can find the selected service for the user, the selected service should
+ * be bound.
+ */
+ @Test
+ public void testSwitchThenUnlockMultipleUsers() throws Exception {
+ final int lastUserId = 5;
+ final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
+ doReturn(pi).when(mIpm).getServiceInfo(any(), anyInt(), anyInt());
+
+ final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ final ParceledListSlice ris =
+ mIpm.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(sContext.getContentResolver()),
+ PackageManager.GET_META_DATA, 0);
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
+ eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
+
+ for (int userId = 1; userId <= lastUserId; userId++) {
+ mService.switchUser(userId, null);
+ verifyLastWallpaperData(userId, sImageWallpaperComponentName);
+ // Simulate user unlocked
+ doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyInt(), eq(userId));
+ mService.onUnlockUser(userId);
+ verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
+ verifyCurrentSystemData(userId);
+ }
+ verifyNoConnectionBeforeLastUser(lastUserId);
+ verifyDisplayData();
+ }
+
+ // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
+ // non-current user must not bind to wallpaper service.
+ private void verifyNoConnectionBeforeLastUser(int lastUserId) {
+ for (int i = 0; i < lastUserId; i++) {
+ final WallpaperData userData = mService.getCurrentWallpaperData(FLAG_SYSTEM, i);
+ assertNull("No user data connection left", userData.connection);
+ }
+ }
+
+ private void verifyLastWallpaperData(int lastUserId, ComponentName expectedComponent) {
+ final WallpaperData lastData = mService.mLastWallpaper;
+ assertNotNull("Last wallpaper must not be null", lastData);
+ assertEquals("Last wallpaper component must be equals.", expectedComponent,
+ lastData.wallpaperComponent);
+ assertEquals("The user id in last wallpaper should be the last switched user",
+ lastUserId, lastData.userId);
+ assertNotNull("Must exist user data connection on last wallpaper data",
+ lastData.connection);
+ }
+
+ private void verifyCurrentSystemData(int userId) {
+ final WallpaperData lastData = mService.mLastWallpaper;
+ final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, userId);
+ assertEquals("Last wallpaper should be equals to current system wallpaper",
+ lastData, wallpaper);
+ }
+
+ private void verifyDisplayData() {
+ mService.forEachDisplayData(data -> {
+ assertTrue("Display width must larger than maximum screen size",
+ data.mWidth >= DISPLAY_SIZE_DIMENSION);
+ assertTrue("Display height must larger than maximum screen size",
+ data.mHeight >= DISPLAY_SIZE_DIMENSION);
+ });
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 356423ad0590..73dcb98abc8c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -111,6 +111,8 @@ public class AbstractAccessibilityServiceConnectionTest {
private static final String VIEW_TEXT = "test_view_text";
private static final int WINDOWID = 12;
private static final int PIP_WINDOWID = 13;
+ private static final int WINDOWID_ONSECONDDISPLAY = 14;
+ private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
private static final int SERVICE_ID = 42;
private static final int A11Y_SERVICE_CAPABILITY = CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
@@ -135,6 +137,7 @@ public class AbstractAccessibilityServiceConnectionTest {
private AbstractAccessibilityServiceConnection mServiceConnection;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
private final List<AccessibilityWindowInfo> mA11yWindowInfos = new ArrayList<>();
+ private final List<AccessibilityWindowInfo> mA11yWindowInfosOnSecondDisplay = new ArrayList<>();
private Callable[] mFindA11yNodesFunctions;
private Callable<Boolean> mPerformA11yAction;
@@ -177,14 +180,22 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
// Fake a11yWindowInfo and remote a11y connection for tests.
- addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false);
- addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true);
+ addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false, Display.DEFAULT_DISPLAY);
+ addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
+ addA11yWindowInfo(mA11yWindowInfosOnSecondDisplay, WINDOWID_ONSECONDDISPLAY, false,
+ SECONDARY_DISPLAY_ID);
when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
.thenReturn(mA11yWindowInfos);
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
.thenReturn(mA11yWindowInfos.get(0));
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(PIP_WINDOWID))
.thenReturn(mA11yWindowInfos.get(1));
+ when(mMockA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(USER_ID,
+ WINDOWID_ONSECONDDISPLAY)).thenReturn(SECONDARY_DISPLAY_ID);
+ when(mMockA11yWindowManager.getWindowListLocked(SECONDARY_DISPLAY_ID))
+ .thenReturn(mA11yWindowInfosOnSecondDisplay);
+ when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID_ONSECONDDISPLAY))
+ .thenReturn(mA11yWindowInfosOnSecondDisplay.get(0));
final RemoteAccessibilityConnection conn = getRemoteA11yConnection(
WINDOWID, mMockIA11yInteractionConnection, PACKAGE_NAME1);
final RemoteAccessibilityConnection connPip = getRemoteA11yConnection(
@@ -327,6 +338,12 @@ public class AbstractAccessibilityServiceConnectionTest {
}
@Test
+ public void getWindow_onNonDefaultDisplay() {
+ assertThat(mServiceConnection.getWindow(WINDOWID_ONSECONDDISPLAY),
+ is(mA11yWindowInfosOnSecondDisplay.get(0)));
+ }
+
+ @Test
public void accessAccessibilityNodeInfo_whenCantGetInfo_returnNullOrFalse()
throws Exception {
when(mMockSecurityPolicy.canGetAccessibilityNodeInfoLocked(
@@ -674,9 +691,10 @@ public class AbstractAccessibilityServiceConnectionTest {
}
private AccessibilityWindowInfo addA11yWindowInfo(List<AccessibilityWindowInfo> infos,
- int windowId, boolean isPip) {
+ int windowId, boolean isPip, int displayId) {
final AccessibilityWindowInfo info = AccessibilityWindowInfo.obtain();
info.setId(windowId);
+ info.setDisplayId(displayId);
info.setPictureInPicture(isPip);
infos.add(info);
return info;
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 6678a7833f9a..6a0d7f192adb 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,21 +20,15 @@ import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
-import static com.android.server.am.MemoryStatUtil.parseIonHeapSizeFromDebugfs;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
-import static com.android.server.am.MemoryStatUtil.parseProcessIonHeapSizesFromDebugfs;
import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import androidx.test.filters.SmallTest;
-import com.android.server.am.MemoryStatUtil.IonAllocations;
-
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -183,71 +177,6 @@ public class MemoryStatUtilTest {
+ "voluntary_ctxt_switches:\t903\n"
+ "nonvoluntary_ctxt_switches:\t104\n";
- // Repeated lines have been removed.
- private static final String DEBUG_SYSTEM_ION_HEAP_CONTENTS = String.join(
- "\n",
- " client pid size",
- "----------------------------------------------------",
- " audio@2.0-servi 765 4096",
- " audio@2.0-servi 765 61440",
- " audio@2.0-servi 765 4096",
- " voip_client 96 8192",
- " voip_client 96 4096",
- " system_server 1232 16728064",
- " surfaceflinger 611 50642944",
- "----------------------------------------------------",
- "orphaned allocations (info is from last known client):",
- "----------------------------------------------------",
- " total orphaned 0",
- " total 55193600",
- " deferred free 0",
- "----------------------------------------------------",
- "0 order 4 highmem pages in uncached pool = 0 total",
- "0 order 4 lowmem pages in uncached pool = 0 total",
- "1251 order 4 lowmem pages in cached pool = 81985536 total",
- "VMID 8: 0 order 4 highmem pages in secure pool = 0 total",
- "VMID 8: 0 order 4 lowmem pages in secure pool = 0 total",
- "--------------------------------------------",
- "uncached pool = 4096 cached pool = 83566592 secure pool = 0",
- "pool total (uncached + cached + secure) = 83570688",
- "--------------------------------------------");
-
- // Repeated lines have been removed.
- private static final String DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO = String.join(
- "\n",
- " client pid size page counts"
- + "-------------------------------------------------- 4K 8K "
- + "16K 32K 64K 128K 256K 512K 1M 2M "
- + "4M >=8M",
- " system_server 1705 58097664 13120 532 "
- + "0 0 0 0 0 0 0 0 "
- + "0 0M",
- " audio@2.0-servi 851 16384 0 2 0 "
- + "0 0 0 0 0 0 0 "
- + "0 0M",
- " audio@2.0-servi 851 4096 1 0 0 "
- + " 0 0 0 0 0 0 0 0 "
- + "0M",
- " audio@2.0-servi 851 4096 1 0 "
- + " 0 0 0 0 0 0 0 0 "
- + "0 0M",
- "----------------------------------------------------",
- "orphaned allocations (info is from last known client):",
- "----------------------------------------------------",
- " total orphaned 0",
- " total 159928320",
- " deferred free 0",
- "----------------------------------------------------",
- "0 order 4 highmem pages in uncached pool = 0 total",
- "0 order 4 lowmem pages in uncached pool = 0 total",
- "1251 order 4 lowmem pages in cached pool = 81985536 total",
- "VMID 8: 0 order 4 highmem pages in secure pool = 0 total",
- "VMID 8: 0 order 4 lowmem pages in secure pool = 0 total",
- "--------------------------------------------",
- "uncached pool = 4096 cached pool = 83566592 secure pool = 0",
- "pool total (uncached + cached + secure) = 83570688",
- "--------------------------------------------");
-
@Test
public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
@@ -302,7 +231,7 @@ public class MemoryStatUtilTest {
@Test
public void testParseVmHWMFromProcfs_parsesCorrectValue() {
- assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE);
+ assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS));
}
@Test
@@ -348,65 +277,4 @@ public class MemoryStatUtilTest {
output.write(bytes, 0, bytes.length);
return output.toString();
}
-
- @Test
- public void testParseIonHeapSizeFromDebugfs_emptyContents() {
- assertEquals(0, parseIonHeapSizeFromDebugfs(""));
-
- assertEquals(0, parseIonHeapSizeFromDebugfs(null));
- }
-
- @Test
- public void testParseIonHeapSizeFromDebugfs_invalidValue() {
- assertEquals(0, parseIonHeapSizeFromDebugfs("<<no-value>>"));
-
- assertEquals(0, parseIonHeapSizeFromDebugfs("\ntotal 12345678901234567890\n"));
- }
-
- @Test
- public void testParseIonHeapSizeFromDebugfs_correctValue() {
- assertEquals(55193600, parseIonHeapSizeFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS));
-
- assertEquals(159928320, parseIonHeapSizeFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO));
- }
-
- @Test
- public void testParseProcessIonHeapSizesFromDebugfs_emptyContents() {
- assertEquals(0, parseProcessIonHeapSizesFromDebugfs("").size());
-
- assertEquals(0, parseProcessIonHeapSizesFromDebugfs(null).size());
- }
-
- @Test
- public void testParseProcessIonHeapSizesFromDebugfs_invalidValue() {
- assertEquals(0, parseProcessIonHeapSizesFromDebugfs("<<no-value>>").size());
- }
-
- @Test
- public void testParseProcessIonHeapSizesFromDebugfs_correctValue1() {
- assertThat(parseProcessIonHeapSizesFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS))
- .containsExactly(
- createIonAllocations(765, 61440 + 4096 + 4096, 3, 61440),
- createIonAllocations(96, 8192 + 4096, 2, 8192),
- createIonAllocations(1232, 16728064, 1, 16728064),
- createIonAllocations(611, 50642944, 1, 50642944));
- }
-
- @Test
- public void testParseProcessIonHeapSizesFromDebugfs_correctValue2() {
- assertThat(parseProcessIonHeapSizesFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO))
- .containsExactly(
- createIonAllocations(1705, 58097664, 1, 58097664),
- createIonAllocations(851, 16384 + 4096 + 4096, 3, 16384));
- }
-
- private static IonAllocations createIonAllocations(int pid, long totalSizeInBytes, int count,
- long maxSizeInBytes) {
- IonAllocations allocations = new IonAllocations();
- allocations.pid = pid;
- allocations.totalSizeInBytes = totalSizeInBytes;
- allocations.count = count;
- allocations.maxSizeInBytes = maxSizeInBytes;
- return allocations;
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 66d2baba2909..70650de2a4b7 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -89,13 +89,11 @@ public class AppOpsUpgradeTest {
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
for(int i = 0; i < uidStates.size(); i++) {
final AppOpsService.UidState uidState = uidStates.valueAt(i);
- if (uidState.opModes != null) {
- final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
- final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
- assertEquals(uidMode1, uidMode2);
- if (uidMode1 != defaultModeOp1) {
- numberOfNonDefaultOps++;
- }
+ final int uidMode1 = uidState.hasOpMode(op1) ? uidState.getOpMode(op1) : defaultModeOp1;
+ final int uidMode2 = uidState.hasOpMode(op2) ? uidState.getOpMode(op2) : defaultModeOp2;
+ assertEquals(uidMode1, uidMode2);
+ if (uidMode1 != defaultModeOp1) {
+ numberOfNonDefaultOps++;
}
if (uidState.pkgOps == null) {
continue;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
index bd3d9ab2220d..3852b9fec001 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java
@@ -17,6 +17,7 @@ package com.android.server.pm;
import android.content.Context;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageManager;
import android.test.InstrumentationTestCase;
import com.android.frameworks.servicestests.R;
@@ -28,7 +29,7 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase {
public void testSuccessfulParse() {
ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
- List<ModuleInfo> mi = provider.getInstalledModules(0);
+ List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL);
assertEquals(2, mi.size());
Collections.sort(mi, (ModuleInfo m1, ModuleInfo m2) ->
@@ -49,18 +50,18 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase {
public void testParseFailure_incorrectTopLevelElement() {
ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata1);
- assertEquals(0, provider.getInstalledModules(0).size());
+ assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size());
}
public void testParseFailure_incorrectModuleElement() {
ModuleInfoProvider provider = getProvider(R.xml.unparseable_metadata2);
- assertEquals(0, provider.getInstalledModules(0).size());
+ assertEquals(0, provider.getInstalledModules(PackageManager.MATCH_ALL).size());
}
public void testParse_unknownAttributesIgnored() {
ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata);
- List<ModuleInfo> mi = provider.getInstalledModules(0);
+ List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL);
assertEquals(2, mi.size());
ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0f077f36faeb..3fe9b52b8415 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -46,6 +46,7 @@ import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.AtomicFile;
import android.util.Log;
import android.util.LongSparseArray;
@@ -53,7 +54,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.os.AtomicFile;
import com.android.server.LocalServices;
import com.android.server.pm.permission.PermissionSettings;
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index d5d32bdd00cc..2290ef79da78 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -18,7 +18,10 @@ package com.android.server.pm;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.opToName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,7 +42,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
-import android.media.AudioAttributes;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.Handler;
@@ -57,6 +59,7 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -79,6 +82,7 @@ import java.util.concurrent.atomic.AtomicReference;
@RunWith(AndroidJUnit4.class)
@LargeTest
+@FlakyTest
public class SuspendPackagesTest {
private static final String TAG = SuspendPackagesTest.class.getSimpleName();
private static final String TEST_APP_LABEL = "Suspend Test App";
@@ -551,28 +555,42 @@ public class SuspendPackagesTest {
}
@Test
- public void testAudioOpBlockedOnSuspend() throws Exception {
+ public void testCameraBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_CAMERA);
+ }
+
+ @Test
+ public void testPlayAudioBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_PLAY_AUDIO);
+ }
+
+ @Test
+ public void testRecordAudioBlockedOnSuspend() throws Exception {
+ assertOpBlockedOnSuspend(OP_RECORD_AUDIO);
+ }
+
+ private void assertOpBlockedOnSuspend(int code) throws Exception {
final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
final CountDownLatch latch = new CountDownLatch(1);
final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
@Override
public void opChanged(int op, int uid, String packageName) {
- if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) {
+ if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) {
latch.countDown();
}
}
};
- iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher);
+ iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
- int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
- AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
- assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode);
+ int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+ assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
+ opMode);
suspendTestPackage(null, null, null);
assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
- audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
- AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
- assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode);
+ opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+ assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
+ opMode);
iAppOps.stopWatchingMode(watcher);
}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
new file mode 100644
index 000000000000..d27f1c7e0ce7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+
+@RunWith(JUnit4.class)
+public class RollbackUnitTest {
+
+ @Test
+ public void newEmptyStagedRollbackDefaults() {
+ int rollbackId = 123;
+ int sessionId = 567;
+ File file = new File("/test/testing");
+
+ Rollback rollback = new Rollback(rollbackId, file, sessionId);
+
+ assertThat(rollback.isEnabling()).isTrue();
+ assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
+ assertThat(rollback.isStaged()).isTrue();
+ assertThat(rollback.getStagedSessionId()).isEqualTo(567);
+ }
+
+ @Test
+ public void newEmptyNonStagedRollbackDefaults() {
+ int rollbackId = 123;
+ File file = new File("/test/testing");
+
+ Rollback rollback = new Rollback(rollbackId, file, -1);
+
+ assertThat(rollback.isEnabling()).isTrue();
+ assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
+ assertThat(rollback.isStaged()).isFalse();
+ }
+
+ @Test
+ public void rollbackStateChanges() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+
+ assertThat(rollback.isEnabling()).isTrue();
+ assertThat(rollback.isAvailable()).isFalse();
+ assertThat(rollback.isCommitted()).isFalse();
+
+ rollback.setAvailable();
+
+ assertThat(rollback.isEnabling()).isFalse();
+ assertThat(rollback.isAvailable()).isTrue();
+ assertThat(rollback.isCommitted()).isFalse();
+
+ rollback.setCommitted();
+
+ assertThat(rollback.isEnabling()).isFalse();
+ assertThat(rollback.isAvailable()).isFalse();
+ assertThat(rollback.isCommitted()).isTrue();
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
new file mode 100644
index 000000000000..8cbf8e56edeb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats;
+
+import static com.android.server.stats.IonMemoryUtil.parseIonHeapSizeFromDebugfs;
+import static com.android.server.stats.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.stats.IonMemoryUtil.IonAllocations;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:IonMemoryUtilTest
+ */
+@SmallTest
+public class IonMemoryUtilTest {
+ // Repeated lines have been removed.
+ private static final String DEBUG_SYSTEM_ION_HEAP_CONTENTS = String.join(
+ "\n",
+ " client pid size",
+ "----------------------------------------------------",
+ " audio@2.0-servi 765 4096",
+ " audio@2.0-servi 765 61440",
+ " audio@2.0-servi 765 4096",
+ " voip_client 96 8192",
+ " voip_client 96 4096",
+ " system_server 1232 16728064",
+ " surfaceflinger 611 50642944",
+ "----------------------------------------------------",
+ "orphaned allocations (info is from last known client):",
+ "----------------------------------------------------",
+ " total orphaned 0",
+ " total 55193600",
+ " deferred free 0",
+ "----------------------------------------------------",
+ "0 order 4 highmem pages in uncached pool = 0 total",
+ "0 order 4 lowmem pages in uncached pool = 0 total",
+ "1251 order 4 lowmem pages in cached pool = 81985536 total",
+ "VMID 8: 0 order 4 highmem pages in secure pool = 0 total",
+ "VMID 8: 0 order 4 lowmem pages in secure pool = 0 total",
+ "--------------------------------------------",
+ "uncached pool = 4096 cached pool = 83566592 secure pool = 0",
+ "pool total (uncached + cached + secure) = 83570688",
+ "--------------------------------------------");
+
+ // Repeated lines have been removed.
+ private static final String DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO = String.join(
+ "\n",
+ " client pid size page counts"
+ + "-------------------------------------------------- 4K 8K "
+ + "16K 32K 64K 128K 256K 512K 1M 2M "
+ + "4M >=8M",
+ " system_server 1705 58097664 13120 532 "
+ + "0 0 0 0 0 0 0 0 "
+ + "0 0M",
+ " audio@2.0-servi 851 16384 0 2 0 "
+ + "0 0 0 0 0 0 0 "
+ + "0 0M",
+ " audio@2.0-servi 851 4096 1 0 0 "
+ + " 0 0 0 0 0 0 0 0 "
+ + "0M",
+ " audio@2.0-servi 851 4096 1 0 "
+ + " 0 0 0 0 0 0 0 0 "
+ + "0 0M",
+ "----------------------------------------------------",
+ "orphaned allocations (info is from last known client):",
+ "----------------------------------------------------",
+ " total orphaned 0",
+ " total 159928320",
+ " deferred free 0",
+ "----------------------------------------------------",
+ "0 order 4 highmem pages in uncached pool = 0 total",
+ "0 order 4 lowmem pages in uncached pool = 0 total",
+ "1251 order 4 lowmem pages in cached pool = 81985536 total",
+ "VMID 8: 0 order 4 highmem pages in secure pool = 0 total",
+ "VMID 8: 0 order 4 lowmem pages in secure pool = 0 total",
+ "--------------------------------------------",
+ "uncached pool = 4096 cached pool = 83566592 secure pool = 0",
+ "pool total (uncached + cached + secure) = 83570688",
+ "--------------------------------------------");
+
+ @Test
+ public void testParseIonHeapSizeFromDebugfs_emptyContents() {
+ assertThat(parseIonHeapSizeFromDebugfs("")).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseIonHeapSizeFromDebugfs_invalidValue() {
+ assertThat(parseIonHeapSizeFromDebugfs("<<no-value>>")).isEqualTo(0);
+
+ assertThat(parseIonHeapSizeFromDebugfs("\ntotal 12345678901234567890\n")).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseIonHeapSizeFromDebugfs_correctValue() {
+ assertThat(parseIonHeapSizeFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS))
+ .isEqualTo(55193600);
+
+ assertThat(parseIonHeapSizeFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO))
+ .isEqualTo(159928320);
+ }
+
+ @Test
+ public void testParseProcessIonHeapSizesFromDebugfs_emptyContents() {
+ assertThat(parseProcessIonHeapSizesFromDebugfs("")).hasSize(0);
+ }
+
+ @Test
+ public void testParseProcessIonHeapSizesFromDebugfs_invalidValue() {
+ assertThat(parseProcessIonHeapSizesFromDebugfs("<<no-value>>").size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseProcessIonHeapSizesFromDebugfs_correctValue1() {
+ assertThat(parseProcessIonHeapSizesFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS))
+ .containsExactly(
+ createIonAllocations(765, 61440 + 4096 + 4096, 3, 61440),
+ createIonAllocations(96, 8192 + 4096, 2, 8192),
+ createIonAllocations(1232, 16728064, 1, 16728064),
+ createIonAllocations(611, 50642944, 1, 50642944));
+ }
+
+ @Test
+ public void testParseProcessIonHeapSizesFromDebugfs_correctValue2() {
+ assertThat(parseProcessIonHeapSizesFromDebugfs(DEBUG_SYSTEM_ION_HEAP_CONTENTS_SARGO))
+ .containsExactly(
+ createIonAllocations(1705, 58097664, 1, 58097664),
+ createIonAllocations(851, 16384 + 4096 + 4096, 3, 16384));
+ }
+
+ private static IonAllocations createIonAllocations(int pid, long totalSizeInBytes, int count,
+ long maxSizeInBytes) {
+ IonAllocations allocations = new IonAllocations();
+ allocations.pid = pid;
+ allocations.totalSizeInBytes = totalSizeInBytes;
+ allocations.count = count;
+ allocations.maxSizeInBytes = maxSizeInBytes;
+ return allocations;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8aaf29a11033..7b7470cca85a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -58,13 +57,13 @@ import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
import com.android.server.UiServiceTestCase;
import com.google.android.collect.Lists;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -76,6 +75,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -331,6 +331,147 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
}
+ /** Test that restore ignores the user id attribute and applies the data to the target user. */
+ @Test
+ public void testWriteReadXml_writeReadDefaults() throws Exception {
+ // setup
+ ManagedServices service1 =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ ManagedServices service2 =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ BufferedOutputStream outStream = new BufferedOutputStream(baos);
+ serializer.setOutput(outStream, "utf-8");
+
+ //data setup
+ service1.addDefaultComponentOrPackage("package/class");
+ serializer.startDocument(null, true);
+ service1.writeXml(serializer, false, 0);
+ serializer.endDocument();
+ outStream.flush();
+
+ final XmlPullParser parser = Xml.newPullParser();
+ BufferedInputStream input = new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray()));
+
+ parser.setInput(input, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, "test");
+ service2.readXml(parser, null, false, 0);
+ ArraySet<ComponentName> defaults = service2.getDefaultComponents();
+
+ assertEquals(1, defaults.size());
+ assertEquals(new ComponentName("package", "class"), defaults.valueAt(0));
+
+ }
+
+ @Test
+ public void resetPackage_enableDefaultsOnly() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 0, true);
+ service.addDefaultComponentOrPackage("package/default");
+ service.addDefaultComponentOrPackage("package2/default");
+
+ ArrayMap<Boolean, ArrayList<ComponentName>> componentsToActivate =
+ service.resetComponents("package", 0);
+
+ assertEquals(1, componentsToActivate.get(true).size());
+ assertEquals(new ComponentName("package", "default"),
+ componentsToActivate.get(true).get(0));
+ }
+
+
+ @Test
+ public void resetPackage_nonDefaultsRemoved() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 0, true);
+ service.addDefaultComponentOrPackage("package/default");
+ service.addDefaultComponentOrPackage("package2/default");
+
+ ArrayMap<Boolean, ArrayList<ComponentName>> componentsToActivate =
+ service.resetComponents("package", 0);
+
+ assertEquals(1, componentsToActivate.get(true).size());
+ assertEquals(new ComponentName("package", "not-default"),
+ componentsToActivate.get(false).get(0));
+ }
+
+ @Test
+ public void resetPackage_onlyDefaultsOnly() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 0, true);
+ service.addDefaultComponentOrPackage("package/default");
+ service.addDefaultComponentOrPackage("package2/default");
+
+ assertEquals(3, service.getAllowedComponents(0).size());
+
+ service.resetComponents("package", 0);
+
+ List<ComponentName> components = service.getAllowedComponents(0);
+ assertEquals(3, components.size());
+ assertTrue(components.contains(new ComponentName("package", "default")));
+ }
+
+ @Test
+ public void resetPackage_affectCurrentUserOnly() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 0, true);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 1, true);
+ service.addDefaultComponentOrPackage("package/default");
+ service.addDefaultComponentOrPackage("package2/default");
+
+ service.resetComponents("package", 0);
+
+ List<ComponentName> components = service.getAllowedComponents(1);
+ assertEquals(3, components.size());
+ }
+
+ @Test
+ public void resetPackage_samePackageMultipleClasses() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ service.addApprovedList(
+ "package/not-default:another-package/not-default:package2/default",
+ 0, true);
+ service.addApprovedList(
+ "package/class:another-package/class:package2/class",
+ 0, true);
+ service.addDefaultComponentOrPackage("package/default");
+ service.addDefaultComponentOrPackage("package2/default");
+
+ service.resetComponents("package", 0);
+
+ List<ComponentName> components = service.getAllowedComponents(0);
+ assertEquals(5, components.size());
+ assertTrue(components.contains(new ComponentName("package", "default")));
+ }
+
/** Test that backup only writes packages/components that belong to the target user. */
@Test
public void testWriteXml_onlyBackupsForTargetUser() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d11995a82229..2de8d0579ec4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -379,6 +379,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Setup managed services
mListener = mListeners.new ManagedServiceInfo(
null, new ComponentName(PKG, "test_class"), mUid, true, null, 0);
+ ComponentName defaultComponent = ComponentName.unflattenFromString("config/device");
+ ArraySet<ComponentName> components = new ArraySet<>();
+ components.add(defaultComponent);
+ when(mListeners.getDefaultComponents()).thenReturn(components);
+ when(mConditionProviders.getDefaultPackages())
+ .thenReturn(new ArraySet<>(Arrays.asList("config")));
+ when(mAssistants.getDefaultComponents()).thenReturn(components);
+ when(mAssistants.queryPackageForServices(
+ anyString(), anyInt(), anyInt())).thenReturn(components);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
ManagedServices.Config listenerConfig = new ManagedServices.Config();
listenerConfig.xmlTag = NotificationListeners.TAG_ENABLED_NOTIFICATION_LISTENERS;
@@ -422,6 +431,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.getUiAutomation().dropShellPermissionIdentity();
}
+ private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
+ ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
+ changed.put(true, new ArrayList<>());
+ changed.put(false, new ArrayList<>());
+ return changed;
+ }
private ApplicationInfo getApplicationInfo(String pkg, int uid) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
@@ -1064,6 +1079,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
+ throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags =
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+ waitForIdle();
+ verify(mListeners, times(1)).notifyPostedLocked(any(), any());
+ verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), any());
+ }
+
+ @Test
public void testUserInitiatedClearAll_noLeak() throws Exception {
final NotificationRecord n = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
@@ -3275,6 +3304,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testBackup() throws Exception {
int systemChecks = mService.countSystemChecks;
+ when(mListeners.queryPackageForServices(anyString(), anyInt(), anyInt()))
+ .thenReturn(new ArraySet<>());
mBinderService.getBackupPayload(1);
assertEquals(1, mService.countSystemChecks - systemChecks);
}
@@ -4468,16 +4499,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void setDefaultAssistantForUser_fromConfigXml() {
clearDeviceConfig();
ComponentName xmlConfig = new ComponentName("config", "xml");
+ ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(xmlConfig));
when(mResources
.getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(xmlConfig.flattenToString());
when(mContext.getResources()).thenReturn(mResources);
- when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
- .thenReturn(Collections.singleton(xmlConfig));
+ when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt()))
+ .thenReturn(components);
+ when(mAssistants.getDefaultComponents())
+ .thenReturn(components);
mService.setNotificationAssistantAccessGrantedCallback(
mNotificationAssistantAccessGrantedCallback);
+
mService.setDefaultAssistantForUser(0);
verify(mNotificationAssistantAccessGrantedCallback)
@@ -4493,8 +4528,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.getString(com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(xmlConfig.flattenToString());
when(mContext.getResources()).thenReturn(mResources);
- when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
+ when(mAssistants.queryPackageForServices(eq(null), anyInt(), anyInt()))
.thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
+ when(mAssistants.getDefaultComponents())
+ .thenReturn(new ArraySet<>(Arrays.asList(deviceConfig)));
mService.setNotificationAssistantAccessGrantedCallback(
mNotificationAssistantAccessGrantedCallback);
@@ -4515,7 +4552,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mContext.getResources()).thenReturn(mResources);
// Only xmlConfig is valid, deviceConfig is not.
when(mAssistants.queryPackageForServices(eq(null), anyInt(), eq(0)))
- .thenReturn(Collections.singleton(xmlConfig));
+ .thenReturn(new ArraySet<>(Collections.singleton(xmlConfig)));
+ when(mAssistants.getDefaultComponents())
+ .thenReturn(new ArraySet<>(Arrays.asList(xmlConfig, deviceConfig)));
mService.setNotificationAssistantAccessGrantedCallback(
mNotificationAssistantAccessGrantedCallback);
@@ -4526,6 +4565,53 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void clearMultipleDefaultAssistantPackagesShouldEnableOnlyOne() throws RemoteException {
+ ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
+ generateResetComponentValues();
+ when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
+ ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
+ ComponentName deviceConfig1 = new ComponentName("device", "config1");
+ ComponentName deviceConfig2 = new ComponentName("device", "config2");
+ changes.put(true, new ArrayList(Arrays.asList(deviceConfig1, deviceConfig2)));
+ changes.put(false, new ArrayList());
+ when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
+ mService.getBinderService().clearData("device", 0, false);
+ verify(mAssistants, times(1))
+ .setPackageOrComponentEnabled(
+ eq("device/config2"),
+ eq(0), eq(true), eq(false));
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ eq("device"), eq(0), eq(false), eq(true));
+ }
+
+ @Test
+ public void clearDefaultListenersPackageShouldEnableIt() throws RemoteException {
+ ArrayMap<Boolean, ArrayList<ComponentName>> changedAssistants =
+ generateResetComponentValues();
+ when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changedAssistants);
+ ComponentName deviceConfig = new ComponentName("device", "config");
+ ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
+ changes.put(true, new ArrayList(Arrays.asList(deviceConfig)));
+ changes.put(false, new ArrayList());
+ when(mListeners.resetComponents(anyString(), anyInt()))
+ .thenReturn(changes);
+ mService.getBinderService().clearData("device", 0, false);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ eq("device"), eq(0), eq(false), eq(true));
+ }
+
+ @Test
+ public void clearDefaultDnDPackageShouldEnableIt() throws RemoteException {
+ ComponentName deviceConfig = new ComponentName("device", "config");
+ ArrayMap<Boolean, ArrayList<ComponentName>> changed = generateResetComponentValues();
+ when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changed);
+ when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changed);
+ mService.getBinderService().clearData("device", 0, false);
+ verify(mConditionProviders, times(1)).resetPackage(
+ eq("device"), eq(0));
+ }
+
+ @Test
public void testFlagBubble() throws RemoteException {
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7b064908565c..8444ab2ebeb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1060,6 +1060,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// simulates finishing in non-focused stack in split-screen.
final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
stack.getChildAt(0).getChildAt(0).nowVisible = true;
+ stack.getChildAt(0).getChildAt(0).visible = true;
topActivity.completeFinishing("test");
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 34cc0c742005..8393ae0c3aec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -354,7 +354,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean());
doReturn(stack).when(mRootActivityContainer)
- .getLaunchStack(any(), any(), any(), anyBoolean(), any());
+ .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
}
// Set up mock package manager internal and make sure no unmocked methods are called
@@ -501,7 +501,6 @@ public class ActivityStarterTests extends ActivityTestsBase {
final ActivityStarter starter = prepareStarter(0);
final LockTaskController lockTaskController = mService.getLockTaskController();
- doReturn(true).when(lockTaskController).isInLockTaskMode();
doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
final int result = starter.setReason("testTaskModeViolation").execute();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index e6d76329ee83..4f00383d1789 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -140,6 +140,8 @@ class ActivityTestsBase {
private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
private boolean mLaunchTaskBehind;
private int mConfigChanges;
+ private int mLaunchedFromPid;
+ private int mLaunchedFromUid;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -215,6 +217,16 @@ class ActivityTestsBase {
return this;
}
+ ActivityBuilder setLaunchedFromPid(int pid) {
+ mLaunchedFromPid = pid;
+ return this;
+ }
+
+ ActivityBuilder setLaunchedFromUid(int uid) {
+ mLaunchedFromUid = uid;
+ return this;
+ }
+
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -251,10 +263,11 @@ class ActivityTestsBase {
}
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
- 0 /* launchedFromPid */, 0, null, intent, null,
- aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
- 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
- mService.mStackSupervisor, options, null /* sourceRecord */);
+ mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */,
+ null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */,
+ null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/,
+ false /* rootVoiceInteraction */, mService.mStackSupervisor, options,
+ null /* sourceRecord */);
spyOn(activity);
if (mTaskRecord != null) {
// fullscreen value is normally read from resources in ctor, so for testing we need
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 86ee75ebf3df..3e2e4382a68c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -31,14 +31,15 @@ import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
import android.view.InsetsState;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class InsetsSourceProviderTest extends WindowTestsBase {
private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR);
@@ -53,7 +54,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
@Test
public void testPostLayout() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
topBar.getFrameLw().set(0, 0, 500, 100);
topBar.mHasSurface = true;
mProvider.setWindow(topBar, null);
@@ -66,7 +67,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
@Test
public void testPostLayout_invisible() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
mProvider.onPostLayout();
@@ -76,7 +77,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
@Test
public void testPostLayout_frameProvider() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar,
(displayFrames, windowState, rect) -> {
@@ -88,19 +89,32 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
@Test
public void testUpdateControlForTarget() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
mProvider.updateControlForTarget(target, false /* force */);
- assertNotNull(mProvider.getControl());
+ assertNotNull(mProvider.getControl(target));
mProvider.updateControlForTarget(null, false /* force */);
- assertNull(mProvider.getControl());
+ assertNull(mProvider.getControl(target));
+ }
+
+ @Test
+ public void testUpdateControlForFakeTarget() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+ final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ mProvider.updateControlForFakeTarget(target);
+ assertNotNull(mProvider.getControl(target));
+ assertNull(mProvider.getControl(target).getLeash());
+ mProvider.updateControlForFakeTarget(null);
+ assertNull(mProvider.getControl(target));
}
@Test
public void testInsetsModified() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
@@ -113,7 +127,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
@Test
public void testInsetsModified_noControl() {
- final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
topBar.getFrameLw().set(0, 0, 500, 100);
mProvider.setWindow(topBar, null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 55011fb0aa04..94abd346a814 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -62,6 +62,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import android.util.MutableLong;
import android.util.SparseBooleanArray;
@@ -1003,7 +1004,6 @@ public class RecentTasksTest extends ActivityTestsBase {
public void testRecentsComponent_allowApiAccessWithoutPermissions() {
doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
-
// Set the recents component and ensure that the following calls do not fail
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
doTestRecentTasksApis(true /* expectNoSecurityException */);
@@ -1289,10 +1289,10 @@ public class RecentTasksTest extends ActivityTestsBase {
@Override
void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
- int callingUid, boolean allowed, boolean crossUser) {
+ int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
mLastAllowed = allowed;
super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
- callingUid, allowed, crossUser);
+ callingUid, allowed, crossUser, profileIds);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index c67b860b656e..aa97de72e507 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -61,6 +62,7 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -820,6 +822,41 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
/**
+ * Test that {@link RootActivityContainer#getLaunchStack} with the real caller id will get the
+ * expected stack when requesting the activity launch on the secondary display.
+ */
+ @Test
+ public void testGetLaunchStackWithRealCallerId() {
+ // Create a non-system owned virtual display.
+ final DisplayInfo info = new DisplayInfo();
+ mSupervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+ info.type = TYPE_VIRTUAL;
+ info.ownerUid = 100;
+ final TestActivityDisplay secondaryDisplay = TestActivityDisplay.create(mSupervisor, info);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
+
+ // Create an activity with specify the original launch pid / uid.
+ final ActivityRecord r = new ActivityBuilder(mService).setLaunchedFromPid(200)
+ .setLaunchedFromUid(200).build();
+
+ // Simulate ActivityStarter to find a launch stack for requesting the activity to launch
+ // on the secondary display with realCallerId.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
+ 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
+ final ActivityStack result = mRootActivityContainer.getLaunchStack(r, options,
+ null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
+ 300 /* test realCallerUid */);
+
+ // Assert that the stack is returned as expected.
+ assertNotNull(result);
+ assertEquals("The display ID of the stack should same as secondary display ",
+ secondaryDisplay.mDisplayId, result.mDisplayId);
+ }
+
+ /**
* Mock {@link RootActivityContainer#resolveHomeActivity} for returning consistent activity
* info for test cases (the original implementation will resolve from the real package manager).
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index cdd4c2424421..3e316f674dbf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import androidx.test.filters.MediumTest;
@@ -45,6 +46,8 @@ import java.util.ArrayList;
@Presubmit
public class RunningTasksTest extends ActivityTestsBase {
+ private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+
private RunningTasks mRunningTasks;
@Before
@@ -77,7 +80,8 @@ public class RunningTasksTest extends ActivityTestsBase {
final int numFetchTasks = 5;
ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
- displays, -1 /* callingUid */, true /* allowed */, true /*crossUser */);
+ displays, -1 /* callingUid */, true /* allowed */, true /*crossUser */,
+ PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < numFetchTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -87,7 +91,8 @@ public class RunningTasksTest extends ActivityTestsBase {
// and does not crash
tasks.clear();
mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
- displays, -1 /* callingUid */, true /* allowed */, true /* crossUser */);
+ displays, -1 /* callingUid */, true /* allowed */, true /* crossUser */,
+ PROFILE_IDS);
assertThat(tasks).hasSize(numTasks);
for (int i = 0; i < numTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 43c6b3580b39..2fe2c41e0036 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -74,6 +74,7 @@ import com.android.server.am.ActivityManagerService;
import com.android.server.appop.AppOpsService;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.input.InputManagerService;
+import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -100,6 +101,7 @@ public class SystemServicesTestRule implements TestRule {
static int sNextTaskId = 100;
private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);
+ private static final int[] TEST_USER_PROFILE_IDS = {};
private Context mContext;
private StaticMockitoSession mMockitoSession;
@@ -424,6 +426,11 @@ public class SystemServicesTestRule implements TestRule {
doReturn(AppOpsManager.MODE_DEFAULT)
.when(aos).noteOperation(anyInt(), anyInt(), anyString());
+ // UserManagerService
+ final UserManagerService ums = mock(UserManagerService.class);
+ doReturn(ums).when(this).getUserManager();
+ doReturn(TEST_USER_PROFILE_IDS).when(ums).getProfileIds(anyInt(), eq(true));
+
setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));
ams.mActivityTaskManager = this;
ams.mAtmInternal = mInternal;
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index c8c55ca96e94..d7b6b5d0d36a 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -46,6 +46,8 @@ import android.service.usb.UsbProfileGroupSettingsManagerProto;
import android.service.usb.UsbSettingsAccessoryPreferenceProto;
import android.service.usb.UsbSettingsDevicePreferenceProto;
import android.service.usb.UserPackageProto;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -70,6 +72,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.net.ProtocolException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -102,10 +105,20 @@ class UsbProfileGroupSettingsManager {
@GuardedBy("mLock")
private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
+ /** Maps DeviceFilter to set of UserPackages not to ask for launch preference anymore */
+ @GuardedBy("mLock")
+ private final ArrayMap<DeviceFilter, ArraySet<UserPackage>> mDevicePreferenceDeniedMap =
+ new ArrayMap<>();
+
/** Maps AccessoryFilter to user preferred application package */
@GuardedBy("mLock")
private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
+ /** Maps AccessoryFilter to set of UserPackages not to ask for launch preference anymore */
+ @GuardedBy("mLock")
+ private final ArrayMap<AccessoryFilter, ArraySet<UserPackage>> mAccessoryPreferenceDeniedMap =
+ new ArrayMap<>();
+
private final Object mLock = new Object();
/**
@@ -248,11 +261,11 @@ class UsbProfileGroupSettingsManager {
}
/**
- * Remove all defaults for a user.
+ * Remove all defaults and denied packages for a user.
*
- * @param userToRemove The user the defaults belong to.
+ * @param userToRemove The user
*/
- void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
+ void removeUser(@NonNull UserHandle userToRemove) {
synchronized (mLock) {
boolean needToPersist = false;
Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
@@ -277,6 +290,28 @@ class UsbProfileGroupSettingsManager {
}
}
+ int numEntries = mDevicePreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.valueAt(i);
+ for (int j = userPackages.size() - 1; j >= 0; j--) {
+ if (userPackages.valueAt(j).user.equals(userToRemove)) {
+ userPackages.removeAt(j);
+ needToPersist = true;
+ }
+ }
+ }
+
+ numEntries = mAccessoryPreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.valueAt(i);
+ for (int j = userPackages.size() - 1; j >= 0; j--) {
+ if (userPackages.valueAt(j).user.equals(userToRemove)) {
+ userPackages.removeAt(j);
+ needToPersist = true;
+ }
+ }
+ }
+
if (needToPersist) {
scheduleWriteSettingsLocked();
}
@@ -284,7 +319,7 @@ class UsbProfileGroupSettingsManager {
}
private void readPreference(XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ throws IOException, XmlPullParserException {
String packageName = null;
// If not set, assume it to be the parent profile
@@ -317,6 +352,67 @@ class UsbProfileGroupSettingsManager {
XmlUtils.nextElement(parser);
}
+ private void readPreferenceDeniedList(@NonNull XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ if (!XmlUtils.nextElementWithin(parser, outerDepth)) {
+ return;
+ }
+
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("user-package".equals(parser.getName())) {
+ try {
+ int userId = XmlUtils.readIntAttribute(parser, "user");
+
+ String packageName = XmlUtils.readStringAttribute(parser, "package");
+ if (packageName == null) {
+ Slog.e(TAG, "Unable to parse package name");
+ }
+
+ ArraySet<UserPackage> set = mDevicePreferenceDeniedMap.get(filter);
+ if (set == null) {
+ set = new ArraySet<>();
+ mDevicePreferenceDeniedMap.put(filter, set);
+ }
+ set.add(new UserPackage(packageName, UserHandle.of(userId)));
+ } catch (ProtocolException e) {
+ Slog.e(TAG, "Unable to parse user id", e);
+ }
+ }
+ }
+ } else if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("user-package".equals(parser.getName())) {
+ try {
+ int userId = XmlUtils.readIntAttribute(parser, "user");
+
+ String packageName = XmlUtils.readStringAttribute(parser, "package");
+ if (packageName == null) {
+ Slog.e(TAG, "Unable to parse package name");
+ }
+
+ ArraySet<UserPackage> set = mAccessoryPreferenceDeniedMap.get(filter);
+ if (set == null) {
+ set = new ArraySet<>();
+ mAccessoryPreferenceDeniedMap.put(filter, set);
+ }
+ set.add(new UserPackage(packageName, UserHandle.of(userId)));
+ } catch (ProtocolException e) {
+ Slog.e(TAG, "Unable to parse user id", e);
+ }
+ }
+ }
+ }
+
+ while (parser.getDepth() > outerDepth) {
+ parser.nextTag(); // ignore unknown tags
+ }
+ }
+
/**
* Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
* Should only be called by owner.
@@ -373,6 +469,8 @@ class UsbProfileGroupSettingsManager {
String tagName = parser.getName();
if ("preference".equals(tagName)) {
readPreference(parser);
+ } else if ("preference-denied-list".equals(tagName)) {
+ readPreferenceDeniedList(parser);
} else {
XmlUtils.nextElement(parser);
}
@@ -436,6 +534,46 @@ class UsbProfileGroupSettingsManager {
serializer.endTag(null, "preference");
}
+ int numEntries = mDevicePreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ DeviceFilter filter = mDevicePreferenceDeniedMap.keyAt(i);
+ ArraySet<UserPackage> userPackageSet = mDevicePreferenceDeniedMap
+ .valueAt(i);
+ serializer.startTag(null, "preference-denied-list");
+ filter.write(serializer);
+
+ int numUserPackages = userPackageSet.size();
+ for (int j = 0; j < numUserPackages; j++) {
+ UserPackage userPackage = userPackageSet.valueAt(j);
+ serializer.startTag(null, "user-package");
+ serializer.attribute(null, "user",
+ String.valueOf(getSerial(userPackage.user)));
+ serializer.attribute(null, "package", userPackage.packageName);
+ serializer.endTag(null, "user-package");
+ }
+ serializer.endTag(null, "preference-denied-list");
+ }
+
+ numEntries = mAccessoryPreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ AccessoryFilter filter = mAccessoryPreferenceDeniedMap.keyAt(i);
+ ArraySet<UserPackage> userPackageSet =
+ mAccessoryPreferenceDeniedMap.valueAt(i);
+ serializer.startTag(null, "preference-denied-list");
+ filter.write(serializer);
+
+ int numUserPackages = userPackageSet.size();
+ for (int j = 0; j < numUserPackages; j++) {
+ UserPackage userPackage = userPackageSet.valueAt(j);
+ serializer.startTag(null, "user-package");
+ serializer.attribute(null, "user",
+ String.valueOf(getSerial(userPackage.user)));
+ serializer.attribute(null, "package", userPackage.packageName);
+ serializer.endTag(null, "user-package");
+ }
+ serializer.endTag(null, "preference-denied-list");
+ }
+
serializer.endTag(null, "settings");
serializer.endDocument();
@@ -834,6 +972,25 @@ class UsbProfileGroupSettingsManager {
private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
@Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
@Nullable UsbAccessory accessory) {
+ // Remove all matches which are on the denied list
+ ArraySet deniedPackages = null;
+ if (device != null) {
+ deniedPackages = mDevicePreferenceDeniedMap.get(new DeviceFilter(device));
+ } else if (accessory != null) {
+ deniedPackages = mAccessoryPreferenceDeniedMap.get(new AccessoryFilter(accessory));
+ }
+ if (deniedPackages != null) {
+ for (int i = matches.size() - 1; i >= 0; i--) {
+ ResolveInfo match = matches.get(i);
+ String packageName = match.activityInfo.packageName;
+ UserHandle user = UserHandle
+ .getUserHandleForUid(match.activityInfo.applicationInfo.uid);
+ if (deniedPackages.contains(new UserPackage(packageName, user))) {
+ matches.remove(i);
+ }
+ }
+ }
+
// don't show the resolver activity if there are no choices available
if (matches.size() == 0) {
if (accessory != null) {
@@ -1076,6 +1233,156 @@ class UsbProfileGroupSettingsManager {
}
/**
+ * Add package to the denied for handling a device
+ *
+ * @param device the device to add to the denied
+ * @param packageNames the packages to not become handler
+ * @param user the user
+ */
+ void addDevicePackagesToDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
+ @NonNull UserHandle user) {
+ if (packageNames.length == 0) {
+ return;
+ }
+ DeviceFilter filter = new DeviceFilter(device);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages;
+ if (mDevicePreferenceDeniedMap.containsKey(filter)) {
+ userPackages = mDevicePreferenceDeniedMap.get(filter);
+ } else {
+ userPackages = new ArraySet<>();
+ mDevicePreferenceDeniedMap.put(filter, userPackages);
+ }
+
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+ if (!userPackages.contains(userPackage)) {
+ userPackages.add(userPackage);
+ shouldWrite = true;
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+
+ /**
+ * Add package to the denied for handling a accessory
+ *
+ * @param accessory the accessory to add to the denied
+ * @param packageNames the packages to not become handler
+ * @param user the user
+ */
+ void addAccessoryPackagesToDenied(@NonNull UsbAccessory accessory,
+ @NonNull String[] packageNames, @NonNull UserHandle user) {
+ if (packageNames.length == 0) {
+ return;
+ }
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages;
+ if (mAccessoryPreferenceDeniedMap.containsKey(filter)) {
+ userPackages = mAccessoryPreferenceDeniedMap.get(filter);
+ } else {
+ userPackages = new ArraySet<>();
+ mAccessoryPreferenceDeniedMap.put(filter, userPackages);
+ }
+
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+ if (!userPackages.contains(userPackage)) {
+ userPackages.add(userPackage);
+ shouldWrite = true;
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+
+ /**
+ * Remove UserPackage from the denied for handling a device
+ *
+ * @param device the device to remove denied packages from
+ * @param packageName the packages to remove
+ * @param user the user
+ */
+ void removeDevicePackagesFromDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
+ @NonNull UserHandle user) {
+ DeviceFilter filter = new DeviceFilter(device);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.get(filter);
+
+ if (userPackages != null) {
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+
+ if (userPackages.contains(userPackage)) {
+ userPackages.remove(userPackage);
+ shouldWrite = true;
+
+ if (userPackages.size() == 0) {
+ mDevicePreferenceDeniedMap.remove(filter);
+ break;
+ }
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove UserPackage from the denied for handling a accessory
+ *
+ * @param accessory the accessory to remove denied packages from
+ * @param packageName the packages to remove
+ * @param user the user
+ */
+ void removeAccessoryPackagesFromDenied(@NonNull UsbAccessory accessory,
+ @NonNull String[] packageNames, @NonNull UserHandle user) {
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.get(filter);
+
+ if (userPackages != null) {
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+
+ if (userPackages.contains(userPackage)) {
+ userPackages.remove(userPackage);
+ shouldWrite = true;
+
+ if (userPackages.size() == 0) {
+ mAccessoryPreferenceDeniedMap.remove(filter);
+ break;
+ }
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+ }
+
+ /**
* Set a package as default handler for a accessory.
*
* @param accessory The accessory that should be handled by default
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index be32c86f108a..ce6f592e2b0d 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -361,6 +361,78 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public void addDevicePackagesToPreferenceDenied(UsbDevice device, String[] packageNames,
+ UserHandle user) {
+ device = Preconditions.checkNotNull(device);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ user = Preconditions.checkNotNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .addDevicePackagesToDenied(device, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void addAccessoryPackagesToPreferenceDenied(UsbAccessory accessory,
+ String[] packageNames, UserHandle user) {
+ accessory = Preconditions.checkNotNull(accessory);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ user = Preconditions.checkNotNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .addAccessoryPackagesToDenied(accessory, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeDevicePackagesFromPreferenceDenied(UsbDevice device, String[] packageNames,
+ UserHandle user) {
+ device = Preconditions.checkNotNull(device);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ user = Preconditions.checkNotNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .removeDevicePackagesFromDenied(device, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeAccessoryPackagesFromPreferenceDenied(UsbAccessory accessory,
+ String[] packageNames, UserHandle user) {
+ accessory = Preconditions.checkNotNull(accessory);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ user = Preconditions.checkNotNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .removeAccessoryPackagesFromDenied(accessory, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void setDevicePersistentPermission(UsbDevice device, int uid, UserHandle user,
boolean shouldBeGranted) {
device = Preconditions.checkNotNull(device);
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index fbd8782a5d1b..7b677eea6b8f 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -130,7 +130,7 @@ class UsbSettingsManager {
// it from all profile groups.
int numProfileGroups = mSettingsByProfileGroup.size();
for (int i = 0; i < numProfileGroups; i++) {
- mSettingsByProfileGroup.valueAt(i).removeAllDefaultsForUser(userToRemove);
+ mSettingsByProfileGroup.valueAt(i).removeUser(userToRemove);
}
}
}
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 13b7b5ca01c8..db1d305070d9 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -24,5 +24,5 @@ android_app {
"src/SystemServerBenchmarkActivity.java",
"src/TextViewInflationActivity.java",
],
- platform_apis: true,
+ sdk_version: "26", // Android O (8.0) and higher
}
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
index eb582f946958..fa1c4e601f83 100755
--- a/startop/scripts/app_startup/app_startup_runner.py
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -41,11 +41,12 @@ DIR = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.dirname(DIR))
import lib.cmd_utils as cmd_utils
import lib.print_utils as print_utils
-import iorap.compiler as compiler
from app_startup.run_app_with_prefetch import PrefetchAppRunner
import app_startup.lib.args_utils as args_utils
from app_startup.lib.data_frame import DataFrame
from app_startup.lib.perfetto_trace_collector import PerfettoTraceCollector
+from iorap.compiler import CompilerType
+import iorap.compiler as compiler
# The following command line options participate in the combinatorial generation.
# All other arguments have a global effect.
@@ -58,8 +59,6 @@ _RUN_SCRIPT = os.path.join(os.path.dirname(os.path.realpath(__file__)),
CollectorPackageInfo = NamedTuple('CollectorPackageInfo',
[('package', str), ('compiler_filter', str)])
-_COMPILER_SCRIPT = os.path.join(os.path.dirname(os.path.dirname(
- os.path.realpath(__file__))), 'iorap/compiler.py')
# by 2; systrace starts up slowly.
_UNLOCK_SCREEN_SCRIPT = os.path.join(
@@ -135,6 +134,10 @@ def parse_options(argv: List[str] = None):
action='append',
help='The trace duration (milliseconds) in '
'compilation')
+ optional_named.add_argument('--compiler-type', dest='compiler_type',
+ type=CompilerType, choices=list(CompilerType),
+ default=CompilerType.DEVICE,
+ help='The type of compiler.')
return parser.parse_args(argv)
@@ -211,26 +214,26 @@ def parse_run_script_csv_file(csv_file: TextIO) -> DataFrame:
return DataFrame(d)
-def compile_perfetto_trace(inodes_path: str,
+def build_ri_compiler_argv(inodes_path: str,
perfetto_trace_file: str,
- trace_duration: Optional[timedelta]) -> TextIO:
- compiler_trace_file = tempfile.NamedTemporaryFile()
- argv = [_COMPILER_SCRIPT, '-i', inodes_path, '--perfetto-trace',
- perfetto_trace_file, '-o', compiler_trace_file.name]
+ trace_duration: Optional[timedelta]
+ ) -> str:
+ argv = ['-i', inodes_path, '--perfetto-trace',
+ perfetto_trace_file]
if trace_duration is not None:
argv += ['--duration', str(int(trace_duration.total_seconds()
- * PerfettoTraceCollector.MS_PER_SEC))]
+ * PerfettoTraceCollector.MS_PER_SEC))]
print_utils.debug_print(argv)
- compiler.main(argv)
- return compiler_trace_file
+ return argv
def execute_run_using_perfetto_trace(collector_info,
run_combos: Iterable[RunCommandArgs],
simulate: bool,
inodes_path: str,
- timeout: int) -> DataFrame:
+ timeout: int,
+ compiler_type: CompilerType) -> DataFrame:
""" Executes run based on perfetto trace. """
passed, perfetto_trace_file = run_perfetto_collector(collector_info,
timeout,
@@ -244,9 +247,15 @@ def execute_run_using_perfetto_trace(collector_info,
if simulate:
compiler_trace_file = tempfile.NamedTemporaryFile()
else:
- compiler_trace_file = compile_perfetto_trace(inodes_path,
- perfetto_trace_file.name,
- combos.trace_duration)
+ ri_compiler_argv = build_ri_compiler_argv(inodes_path,
+ perfetto_trace_file.name,
+ combos.trace_duration)
+ compiler_trace_file = compiler.compile(compiler_type,
+ inodes_path,
+ ri_compiler_argv,
+ combos.package,
+ combos.activity)
+
with compiler_trace_file:
combos = combos._replace(input=compiler_trace_file.name)
print_utils.debug_print(combos)
@@ -261,7 +270,8 @@ def execute_run_combos(
grouped_run_combos: Iterable[Tuple[CollectorPackageInfo, Iterable[RunCommandArgs]]],
simulate: bool,
inodes_path: str,
- timeout: int):
+ timeout: int,
+ compiler_type: CompilerType):
# nothing will work if the screen isn't unlocked first.
cmd_utils.execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT],
timeout,
@@ -273,7 +283,8 @@ def execute_run_combos(
run_combos,
simulate,
inodes_path,
- timeout)
+ timeout,
+ compiler_type)
def gather_results(commands: Iterable[Tuple[DataFrame]],
key_list: List[str], value_list: List[Tuple[str, ...]]):
@@ -361,7 +372,8 @@ def main():
exec = execute_run_combos(grouped_combos(),
opts.simulate,
opts.inodes,
- opts.timeout)
+ opts.timeout,
+ opts.compiler_type)
results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos())
diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py
index 42ea5f0e4bb9..382f6f3c70ff 100755
--- a/startop/scripts/app_startup/app_startup_runner_test.py
+++ b/startop/scripts/app_startup/app_startup_runner_test.py
@@ -92,7 +92,7 @@ def default_dict_for_parsed_args(**kwargs):
"""
d = {'compiler_filters': None, 'simulate': False, 'debug': False,
'output': None, 'timeout': 10, 'loop_count': 1, 'inodes': None,
- 'trace_duration': None}
+ 'trace_duration': None, 'compiler_type': asr.CompilerType.HOST}
d.update(kwargs)
return d
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
index 17b58c19ef28..1426d34f325d 100755..100644
--- a/startop/scripts/iorap/compiler.py
+++ b/startop/scripts/iorap/compiler.py
@@ -1,323 +1,73 @@
#!/usr/bin/env python3
-
#
-# Copyright (C) 2019 The Android Open Source Project
+# Copyright 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
+# 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.
-#
-#
-# Dependencies:
-#
-# $> sudo apt-get install python3-pip
-# $> pip3 install --user protobuf sqlalchemy sqlite3
-#
-
-import optparse
+import importlib
import os
-import re
import sys
import tempfile
-from pathlib import Path
-from datetime import timedelta
-from typing import Iterable, Optional, List
+from enum import Enum
+from typing import TextIO, List
+# local import
DIR = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.dirname(DIR))
-from iorap.generated.TraceFile_pb2 import *
-from iorap.lib.inode2filename import Inode2Filename
-
-parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-sys.path.append(parent_dir_name)
-from trace_analyzer.lib.trace2db import Trace2Db, MmFilemapAddToPageCache, \
- RawFtraceEntry
-import lib.cmd_utils as cmd_utils
-
-_PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes.
-ANDROID_BUILD_TOP = Path(parent_dir_name).parents[3]
-TRACECONV_BIN = ANDROID_BUILD_TOP.joinpath(
- 'external/perfetto/tools/traceconv')
-
-class PageRun:
- """
- Intermediate representation for a run of one or more pages.
- """
- def __init__(self, device_number: int, inode: int, offset: int, length: int):
- self.device_number = device_number
- self.inode = inode
- self.offset = offset
- self.length = length
-
- def __str__(self):
- return "PageRun(device_number=%d, inode=%d, offset=%d, length=%d)" \
- %(self.device_number, self.inode, self.offset, self.length)
-
-def debug_print(msg):
- #print(msg)
- pass
-
-UNDER_LAUNCH = False
-
-def page_cache_entries_to_runs(page_cache_entries: Iterable[MmFilemapAddToPageCache]):
- global _PAGE_SIZE
-
- runs = [
- PageRun(device_number=pg_entry.dev, inode=pg_entry.ino, offset=pg_entry.ofs,
- length=_PAGE_SIZE)
- for pg_entry in page_cache_entries
- ]
-
- for r in runs:
- debug_print(r)
-
- print("Stats: Page runs totaling byte length: %d" %(len(runs) * _PAGE_SIZE))
-
- return runs
-
-def optimize_page_runs(page_runs):
- new_entries = []
- last_entry = None
- for pg_entry in page_runs:
- if last_entry:
- if pg_entry.device_number == last_entry.device_number and pg_entry.inode == last_entry.inode:
- # we are dealing with a run for the same exact file as a previous run.
- if pg_entry.offset == last_entry.offset + last_entry.length:
- # trivially contiguous entries. merge them together.
- last_entry.length += pg_entry.length
- continue
- # Default: Add the run without merging it to a previous run.
- last_entry = pg_entry
- new_entries.append(pg_entry)
- return new_entries
-
-def is_filename_matching_filter(file_name, filters=[]):
- """
- Blacklist-style regular expression filters.
-
- :return: True iff file_name has an RE match in one of the filters.
- """
- for filt in filters:
- res = re.search(filt, file_name)
- if res:
- return True
-
- return False
-
-def build_protobuf(page_runs, inode2filename, filters=[]):
- trace_file = TraceFile()
- trace_file_index = trace_file.index
-
- file_id_counter = 0
- file_id_map = {} # filename -> id
-
- stats_length_total = 0
- filename_stats = {} # filename -> total size
-
- skipped_inode_map = {}
- filtered_entry_map = {} # filename -> count
-
- for pg_entry in page_runs:
- fn = inode2filename.resolve(pg_entry.device_number, pg_entry.inode)
- if not fn:
- skipped_inode_map[pg_entry.inode] = skipped_inode_map.get(pg_entry.inode, 0) + 1
- continue
-
- filename = fn
-
- if filters and not is_filename_matching_filter(filename, filters):
- filtered_entry_map[filename] = filtered_entry_map.get(filename, 0) + 1
- continue
-
- file_id = file_id_map.get(filename)
- if not file_id:
- file_id = file_id_counter
- file_id_map[filename] = file_id_counter
- file_id_counter = file_id_counter + 1
-
- file_index_entry = trace_file_index.entries.add()
- file_index_entry.id = file_id
- file_index_entry.file_name = filename
-
- # already in the file index, add the file entry.
- file_entry = trace_file.list.entries.add()
- file_entry.index_id = file_id
- file_entry.file_length = pg_entry.length
- stats_length_total += file_entry.file_length
- file_entry.file_offset = pg_entry.offset
-
- filename_stats[filename] = filename_stats.get(filename, 0) + file_entry.file_length
-
- for inode, count in skipped_inode_map.items():
- print("WARNING: Skip inode %s because it's not in inode map (%d entries)" %(inode, count))
-
- print("Stats: Sum of lengths %d" %(stats_length_total))
-
- if filters:
- print("Filter: %d total files removed." %(len(filtered_entry_map)))
-
- for fn, count in filtered_entry_map.items():
- print("Filter: File '%s' removed '%d' entries." %(fn, count))
-
- for filename, file_size in filename_stats.items():
- print("%s,%s" %(filename, file_size))
-
- return trace_file
-
-def calc_trace_end_time(trace2db: Trace2Db,
- trace_duration: Optional[timedelta]) -> float:
- """
- Calculates the end time based on the trace duration.
- The start time is the first receiving mm file map event.
- The end time is the start time plus the trace duration.
- All of them are in milliseconds.
- """
- # If the duration is not set, assume all time is acceptable.
- if trace_duration is None:
- # float('inf')
- return RawFtraceEntry.__table__.c.timestamp.type.python_type('inf')
-
- first_event = trace2db.session.query(MmFilemapAddToPageCache).join(
- MmFilemapAddToPageCache.raw_ftrace_entry).order_by(
- RawFtraceEntry.timestamp).first()
-
- # total_seconds() will return a float number.
- return first_event.raw_ftrace_entry.timestamp + trace_duration.total_seconds()
-
-def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[timedelta]):
- end_time = calc_trace_end_time(trace2db, trace_duration)
- # SELECT * FROM tbl ORDER BY id;
- return trace2db.session.query(MmFilemapAddToPageCache).join(
- MmFilemapAddToPageCache.raw_ftrace_entry).filter(
- RawFtraceEntry.timestamp <= end_time).order_by(
- MmFilemapAddToPageCache.id).all()
-
-def transform_perfetto_trace_to_systrace(path_to_perfetto_trace: str,
- path_to_tmp_systrace: str) -> None:
- """ Transforms the systrace file from perfetto trace. """
- cmd_utils.run_command_nofail([str(TRACECONV_BIN),
- 'systrace',
- path_to_perfetto_trace,
- path_to_tmp_systrace])
-
-
-def run(sql_db_path:str,
- trace_file:str,
- trace_duration:Optional[timedelta],
- output_file:str,
- inode_table:str,
- filter:List[str]) -> int:
- trace2db = Trace2Db(sql_db_path)
- # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache.
- trace2db.set_raw_ftrace_entry_filter(\
- lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache')
- # TODO: parse multiple trace files here.
- parse_count = trace2db.parse_file_into_db(trace_file)
-
- mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db,
- trace_duration)
- print("DONE. Parsed %d entries into sql db." %(len(mm_filemap_add_to_page_cache_rows)))
-
- page_runs = page_cache_entries_to_runs(mm_filemap_add_to_page_cache_rows)
- print("DONE. Converted %d entries" %(len(page_runs)))
-
- # TODO: flags to select optimizations.
- optimized_page_runs = optimize_page_runs(page_runs)
- print("DONE. Optimized down to %d entries" %(len(optimized_page_runs)))
-
- print("Build protobuf...")
- trace_file = build_protobuf(optimized_page_runs, inode_table, filter)
-
- print("Write protobuf to file...")
- output_file = open(output_file, 'wb')
- output_file.write(trace_file.SerializeToString())
- output_file.close()
-
- print("DONE")
-
- # TODO: Silent running mode [no output except on error] for build runs.
-
- return 0
-
-def main(argv):
- parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb")
- parser.add_option('-i', dest='inode_data_file', metavar='FILE',
- help='Read cached inode data from a file saved earlier with pagecache.py -d')
- parser.add_option('-t', dest='trace_file', metavar='FILE',
- help='Path to systrace file (trace.html) that will be parsed')
- parser.add_option('--perfetto-trace', dest='perfetto_trace_file',
- metavar='FILE',
- help='Path to perfetto trace that will be parsed')
-
- parser.add_option('--db', dest='sql_db', metavar='FILE',
- help='Path to intermediate sqlite3 database [default: in-memory].')
-
- parser.add_option('-f', dest='filter', action="append", default=[],
- help="Add file filter. All file entries not matching one of the filters are discarded.")
-
- parser.add_option('-l', dest='launch_lock', action="store_true", default=False,
- help="Exclude all events not inside launch_lock")
-
- parser.add_option('-o', dest='output_file', metavar='FILE',
- help='Output protobuf file')
-
- parser.add_option('--duration', dest='trace_duration', action="store",
- type=int, help='The duration of trace in milliseconds.')
-
- options, categories = parser.parse_args(argv[1:])
-
- # TODO: OptionParser should have some flags to make these mandatory.
- if not options.inode_data_file:
- parser.error("-i is required")
- if not options.trace_file and not options.perfetto_trace_file:
- parser.error("one of -t or --perfetto-trace is required")
- if options.trace_file and options.perfetto_trace_file:
- parser.error("please enter either -t or --perfetto-trace, not both")
- if not options.output_file:
- parser.error("-o is required")
-
- if options.launch_lock:
- print("INFO: Launch lock flag (-l) enabled; filtering all events not inside launch_lock.")
-
- inode_table = Inode2Filename.new_from_filename(options.inode_data_file)
-
- sql_db_path = ":memory:"
- if options.sql_db:
- sql_db_path = options.sql_db
-
- trace_duration = timedelta(milliseconds=options.trace_duration) if \
- options.trace_duration is not None else None
-
- # if the input is systrace
- if options.trace_file:
- return run(sql_db_path,
- options.trace_file,
- trace_duration,
- options.output_file,
- inode_table,
- options.filter)
-
- # if the input is perfetto trace
- # TODO python 3.7 switch to using nullcontext
- with tempfile.NamedTemporaryFile() as trace_file:
- transform_perfetto_trace_to_systrace(options.perfetto_trace_file,
- trace_file.name)
- return run(sql_db_path,
- trace_file.name,
- trace_duration,
- options.output_file,
- inode_table,
- options.filter)
-
-if __name__ == '__main__':
- print(sys.argv)
- sys.exit(main(sys.argv))
+import lib.print_utils as print_utils
+
+# Type of compiler.
+class CompilerType(Enum):
+ HOST = 1 # iorap.cmd.compiler on host
+ DEVICE = 2 # adb shell iorap.cmd.compiler
+ RI = 3 # compiler.py
+
+def compile_perfetto_trace_ri(
+ argv: List[str],
+ compiler) -> TextIO:
+ print_utils.debug_print('Compile using RI compiler.')
+ compiler_trace_file = tempfile.NamedTemporaryFile()
+ argv.extend(['-o', compiler_trace_file.name])
+ print_utils.debug_print(argv)
+ compiler.main([''] + argv)
+ return compiler_trace_file
+
+def compile_perfetto_trace_device(inodes_path: str,
+ package: str,
+ activity: str,
+ compiler) -> TextIO:
+ print_utils.debug_print('Compile using on-device compiler.')
+ compiler_trace_file = tempfile.NamedTemporaryFile()
+ compiler.main(inodes_path, package, activity, compiler_trace_file.name)
+ return compiler_trace_file
+
+def compile(compiler_type: CompilerType,
+ inodes_path: str,
+ ri_compiler_argv,
+ package: str,
+ activity: str) -> TextIO:
+ if compiler_type == CompilerType.RI:
+ compiler = importlib.import_module('iorap.compiler_ri')
+ compiler_trace_file = compile_perfetto_trace_ri(ri_compiler_argv,
+ compiler)
+ return compiler_trace_file
+ if compiler_type == CompilerType.DEVICE:
+ compiler = importlib.import_module('iorap.compiler_device')
+ compiler_trace_file = compile_perfetto_trace_device(inodes_path,
+ package,
+ activity,
+ compiler)
+ return compiler_trace_file
+
+ # Should not arrive here.
+ raise ValueError('Unknown compiler type')
diff --git a/startop/scripts/iorap/compiler_device.py b/startop/scripts/iorap/compiler_device.py
new file mode 100644
index 000000000000..d941cd913fe1
--- /dev/null
+++ b/startop/scripts/iorap/compiler_device.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+import argparse
+import os
+import sys
+from typing import List
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR)) # framework/base/startop/script
+import lib.print_utils as print_utils
+import iorap.lib.iorapd_utils as iorapd_utils
+from app_startup.lib.app_runner import AppRunner
+
+IORAP_COMMON_BASH_SCRIPT = os.path.join(DIR, 'common')
+
+def parse_options(argv: List[str] = None):
+ """Parses command line arguments and returns an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Compile perfetto trace file")
+ required_named = parser.add_argument_group('required named arguments')
+
+ required_named.add_argument('-i', dest='inodes', metavar='FILE',
+ help='Read cached inode data from a file saved '
+ 'earlier with pagecache.py -d')
+ required_named.add_argument('-p', dest='package',
+ help='Package of the app to be compiled')
+
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-o', dest='output',
+ help='The compiled trace is stored into the output file')
+ optional_named.add_argument('-a', dest='activity',
+ help='Activity of the app to be compiled')
+ optional_named.add_argument('-d', dest='debug', action='store_true'
+ , help='Activity of the app to be compiled')
+
+ return parser.parse_args(argv)
+
+def main(inodes, package, activity, output, **kwargs) -> int:
+ """Entries of the program."""
+ if not activity:
+ activity = AppRunner.get_activity(package)
+
+ passed = iorapd_utils.compile_perfetto_trace_on_device(package, activity,
+ inodes)
+ if passed and output:
+ iorapd_utils.get_iorapd_compiler_trace(package, activity, output)
+
+ return 0
+
+if __name__ == '__main__':
+ opts = parse_options()
+ if opts.debug:
+ print_utils.DEBUG = opts.debug
+ print_utils.debug_print(opts)
+ sys.exit(main(**(vars(opts))))
diff --git a/startop/scripts/iorap/compiler_ri.py b/startop/scripts/iorap/compiler_ri.py
new file mode 100755
index 000000000000..90fc8a8123c5
--- /dev/null
+++ b/startop/scripts/iorap/compiler_ri.py
@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+
+#
+# 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.
+#
+
+#
+# Dependencies:
+#
+# $> sudo apt-get install python3-pip
+# $> pip3 install --user protobuf sqlalchemy sqlite3
+#
+
+import optparse
+import os
+import re
+import sys
+import tempfile
+from pathlib import Path
+from datetime import timedelta
+from typing import Iterable, Optional, List
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+from iorap.generated.TraceFile_pb2 import *
+from iorap.lib.inode2filename import Inode2Filename
+
+parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(parent_dir_name)
+from trace_analyzer.lib.trace2db import Trace2Db, MmFilemapAddToPageCache, \
+ RawFtraceEntry
+import lib.cmd_utils as cmd_utils
+
+_PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes.
+ANDROID_BUILD_TOP = Path(parent_dir_name).parents[3]
+TRACECONV_BIN = ANDROID_BUILD_TOP.joinpath(
+ 'external/perfetto/tools/traceconv')
+
+class PageRun:
+ """
+ Intermediate representation for a run of one or more pages.
+ """
+ def __init__(self, device_number: int, inode: int, offset: int, length: int):
+ self.device_number = device_number
+ self.inode = inode
+ self.offset = offset
+ self.length = length
+
+ def __str__(self):
+ return "PageRun(device_number=%d, inode=%d, offset=%d, length=%d)" \
+ %(self.device_number, self.inode, self.offset, self.length)
+
+def debug_print(msg):
+ #print(msg)
+ pass
+
+UNDER_LAUNCH = False
+
+def page_cache_entries_to_runs(page_cache_entries: Iterable[MmFilemapAddToPageCache]):
+ global _PAGE_SIZE
+
+ runs = [
+ PageRun(device_number=pg_entry.dev, inode=pg_entry.ino, offset=pg_entry.ofs,
+ length=_PAGE_SIZE)
+ for pg_entry in page_cache_entries
+ ]
+
+ for r in runs:
+ debug_print(r)
+
+ print("Stats: Page runs totaling byte length: %d" %(len(runs) * _PAGE_SIZE))
+
+ return runs
+
+def optimize_page_runs(page_runs):
+ new_entries = []
+ last_entry = None
+ for pg_entry in page_runs:
+ if last_entry:
+ if pg_entry.device_number == last_entry.device_number and pg_entry.inode == last_entry.inode:
+ # we are dealing with a run for the same exact file as a previous run.
+ if pg_entry.offset == last_entry.offset + last_entry.length:
+ # trivially contiguous entries. merge them together.
+ last_entry.length += pg_entry.length
+ continue
+ # Default: Add the run without merging it to a previous run.
+ last_entry = pg_entry
+ new_entries.append(pg_entry)
+ return new_entries
+
+def is_filename_matching_filter(file_name, filters=[]):
+ """
+ Blacklist-style regular expression filters.
+
+ :return: True iff file_name has an RE match in one of the filters.
+ """
+ for filt in filters:
+ res = re.search(filt, file_name)
+ if res:
+ return True
+
+ return False
+
+def build_protobuf(page_runs, inode2filename, filters=[]):
+ trace_file = TraceFile()
+ trace_file_index = trace_file.index
+
+ file_id_counter = 0
+ file_id_map = {} # filename -> id
+
+ stats_length_total = 0
+ filename_stats = {} # filename -> total size
+
+ skipped_inode_map = {}
+ filtered_entry_map = {} # filename -> count
+
+ for pg_entry in page_runs:
+ fn = inode2filename.resolve(pg_entry.device_number, pg_entry.inode)
+ if not fn:
+ skipped_inode_map[pg_entry.inode] = skipped_inode_map.get(pg_entry.inode, 0) + 1
+ continue
+
+ filename = fn
+
+ if filters and not is_filename_matching_filter(filename, filters):
+ filtered_entry_map[filename] = filtered_entry_map.get(filename, 0) + 1
+ continue
+
+ file_id = file_id_map.get(filename)
+ # file_id could 0, which satisfies "if file_id" and causes duplicate
+ # filename for file id 0.
+ if file_id is None:
+ file_id = file_id_counter
+ file_id_map[filename] = file_id_counter
+ file_id_counter = file_id_counter + 1
+
+ file_index_entry = trace_file_index.entries.add()
+ file_index_entry.id = file_id
+ file_index_entry.file_name = filename
+
+ # already in the file index, add the file entry.
+ file_entry = trace_file.list.entries.add()
+ file_entry.index_id = file_id
+ file_entry.file_length = pg_entry.length
+ stats_length_total += file_entry.file_length
+ file_entry.file_offset = pg_entry.offset
+
+ filename_stats[filename] = filename_stats.get(filename, 0) + file_entry.file_length
+
+ for inode, count in skipped_inode_map.items():
+ print("WARNING: Skip inode %s because it's not in inode map (%d entries)" %(inode, count))
+
+ print("Stats: Sum of lengths %d" %(stats_length_total))
+
+ if filters:
+ print("Filter: %d total files removed." %(len(filtered_entry_map)))
+
+ for fn, count in filtered_entry_map.items():
+ print("Filter: File '%s' removed '%d' entries." %(fn, count))
+
+ for filename, file_size in filename_stats.items():
+ print("%s,%s" %(filename, file_size))
+
+ return trace_file
+
+def calc_trace_end_time(trace2db: Trace2Db,
+ trace_duration: Optional[timedelta]) -> float:
+ """
+ Calculates the end time based on the trace duration.
+ The start time is the first receiving mm file map event.
+ The end time is the start time plus the trace duration.
+ All of them are in milliseconds.
+ """
+ # If the duration is not set, assume all time is acceptable.
+ if trace_duration is None:
+ # float('inf')
+ return RawFtraceEntry.__table__.c.timestamp.type.python_type('inf')
+
+ first_event = trace2db.session.query(MmFilemapAddToPageCache).join(
+ MmFilemapAddToPageCache.raw_ftrace_entry).order_by(
+ RawFtraceEntry.timestamp).first()
+
+ # total_seconds() will return a float number.
+ return first_event.raw_ftrace_entry.timestamp + trace_duration.total_seconds()
+
+def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[timedelta]):
+ end_time = calc_trace_end_time(trace2db, trace_duration)
+ # SELECT * FROM tbl ORDER BY id;
+ return trace2db.session.query(MmFilemapAddToPageCache).join(
+ MmFilemapAddToPageCache.raw_ftrace_entry).filter(
+ RawFtraceEntry.timestamp <= end_time).order_by(
+ MmFilemapAddToPageCache.id).all()
+
+def transform_perfetto_trace_to_systrace(path_to_perfetto_trace: str,
+ path_to_tmp_systrace: str) -> None:
+ """ Transforms the systrace file from perfetto trace. """
+ cmd_utils.run_command_nofail([str(TRACECONV_BIN),
+ 'systrace',
+ path_to_perfetto_trace,
+ path_to_tmp_systrace])
+
+
+def run(sql_db_path:str,
+ trace_file:str,
+ trace_duration:Optional[timedelta],
+ output_file:str,
+ inode_table:str,
+ filter:List[str]) -> int:
+ trace2db = Trace2Db(sql_db_path)
+ # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache.
+ trace2db.set_raw_ftrace_entry_filter(\
+ lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache')
+ # TODO: parse multiple trace files here.
+ parse_count = trace2db.parse_file_into_db(trace_file)
+
+ mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db,
+ trace_duration)
+ print("DONE. Parsed %d entries into sql db." %(len(mm_filemap_add_to_page_cache_rows)))
+
+ page_runs = page_cache_entries_to_runs(mm_filemap_add_to_page_cache_rows)
+ print("DONE. Converted %d entries" %(len(page_runs)))
+
+ # TODO: flags to select optimizations.
+ optimized_page_runs = optimize_page_runs(page_runs)
+ print("DONE. Optimized down to %d entries" %(len(optimized_page_runs)))
+
+ print("Build protobuf...")
+ trace_file = build_protobuf(optimized_page_runs, inode_table, filter)
+
+ print("Write protobuf to file...")
+ output_file = open(output_file, 'wb')
+ output_file.write(trace_file.SerializeToString())
+ output_file.close()
+
+ print("DONE")
+
+ # TODO: Silent running mode [no output except on error] for build runs.
+
+ return 0
+
+def main(argv):
+ parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb")
+ parser.add_option('-i', dest='inode_data_file', metavar='FILE',
+ help='Read cached inode data from a file saved earlier with pagecache.py -d')
+ parser.add_option('-t', dest='trace_file', metavar='FILE',
+ help='Path to systrace file (trace.html) that will be parsed')
+ parser.add_option('--perfetto-trace', dest='perfetto_trace_file',
+ metavar='FILE',
+ help='Path to perfetto trace that will be parsed')
+
+ parser.add_option('--db', dest='sql_db', metavar='FILE',
+ help='Path to intermediate sqlite3 database [default: in-memory].')
+
+ parser.add_option('-f', dest='filter', action="append", default=[],
+ help="Add file filter. All file entries not matching one of the filters are discarded.")
+
+ parser.add_option('-l', dest='launch_lock', action="store_true", default=False,
+ help="Exclude all events not inside launch_lock")
+
+ parser.add_option('-o', dest='output_file', metavar='FILE',
+ help='Output protobuf file')
+
+ parser.add_option('--duration', dest='trace_duration', action="store",
+ type=int, help='The duration of trace in milliseconds.')
+
+ options, categories = parser.parse_args(argv[1:])
+
+ # TODO: OptionParser should have some flags to make these mandatory.
+ if not options.inode_data_file:
+ parser.error("-i is required")
+ if not options.trace_file and not options.perfetto_trace_file:
+ parser.error("one of -t or --perfetto-trace is required")
+ if options.trace_file and options.perfetto_trace_file:
+ parser.error("please enter either -t or --perfetto-trace, not both")
+ if not options.output_file:
+ parser.error("-o is required")
+
+ if options.launch_lock:
+ print("INFO: Launch lock flag (-l) enabled; filtering all events not inside launch_lock.")
+
+ inode_table = Inode2Filename.new_from_filename(options.inode_data_file)
+
+ sql_db_path = ":memory:"
+ if options.sql_db:
+ sql_db_path = options.sql_db
+
+ trace_duration = timedelta(milliseconds=options.trace_duration) if \
+ options.trace_duration is not None else None
+
+ # if the input is systrace
+ if options.trace_file:
+ return run(sql_db_path,
+ options.trace_file,
+ trace_duration,
+ options.output_file,
+ inode_table,
+ options.filter)
+
+ # if the input is perfetto trace
+ # TODO python 3.7 switch to using nullcontext
+ with tempfile.NamedTemporaryFile() as trace_file:
+ transform_perfetto_trace_to_systrace(options.perfetto_trace_file,
+ trace_file.name)
+ return run(sql_db_path,
+ trace_file.name,
+ trace_duration,
+ options.output_file,
+ inode_table,
+ options.filter)
+
+if __name__ == '__main__':
+ print(sys.argv)
+ sys.exit(main(sys.argv))
diff --git a/startop/scripts/iorap/compiler_test.py b/startop/scripts/iorap/compiler_test.py
index 1a9f059fc6b6..d1f11c5da2a5 100644
--- a/startop/scripts/iorap/compiler_test.py
+++ b/startop/scripts/iorap/compiler_test.py
@@ -30,7 +30,7 @@ See also https://docs.pytest.org/en/latest/usage.html
"""
import os
-import compiler
+import compiler_host as compiler
DIR = os.path.abspath(os.path.dirname(__file__))
TEXTCACHE = os.path.join(DIR, 'test_fixtures/compiler/common_textcache')
diff --git a/startop/scripts/iorap/lib/iorapd_utils.py b/startop/scripts/iorap/lib/iorapd_utils.py
index 0d62180a01e3..f6f21fd70005 100644
--- a/startop/scripts/iorap/lib/iorapd_utils.py
+++ b/startop/scripts/iorap/lib/iorapd_utils.py
@@ -18,10 +18,9 @@
import os
import sys
-from pathlib import Path
-# up to two level, like '../../'
-sys.path.append(Path(os.path.abspath(__file__)).parents[2])
+# up to two level
+sys.path.append(os.path.join(os.path.abspath(__file__),'../..'))
import lib.cmd_utils as cmd_utils
IORAPID_LIB_DIR = os.path.abspath(os.path.dirname(__file__))
@@ -39,6 +38,22 @@ def _iorapd_path_to_data_file(package: str, activity: str, suffix: str) -> str:
# Match logic of 'AppComponentName' in iorap::compiler C++ code.
return '{}/{}%2F{}.{}'.format(IORAPD_DATA_PATH, package, activity, suffix)
+def compile_perfetto_trace_on_device(package: str, activity: str,
+ inodes: str) -> bool:
+ """Compiles the perfetto trace using on-device compiler."""
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_compiler_for_app_trace',
+ [package, activity, inodes])
+ return passed
+
+def get_iorapd_compiler_trace(package: str, activity: str, dest: str) -> str:
+ """Gets compiler trace to dest file."""
+ src = _iorapd_path_to_data_file(package, activity, 'compiled_trace.pb')
+ passed, _ = cmd_utils.run_shell_command('adb pull "{}" "{}"'.format(src, dest))
+ if not passed:
+ return False
+ return True
+
def iorapd_compiler_install_trace_file(package: str, activity: str,
input_file: str) -> bool:
"""Installs a compiled trace file.
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/android/telephony/CallerInfo.java
index 13539b855de2..f87ac503bbc5 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/android/telephony/CallerInfo.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.telephony;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -31,10 +33,6 @@ import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.RawContacts;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -43,6 +41,7 @@ import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Locale;
@@ -51,11 +50,14 @@ import java.util.Locale;
*
* {@hide}
*/
+@SystemApi
public class CallerInfo {
private static final String TAG = "CallerInfo";
private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+ /** @hide */
public static final long USER_TYPE_CURRENT = 0;
+ /** @hide */
public static final long USER_TYPE_WORK = 1;
/**
@@ -85,49 +87,61 @@ public class CallerInfo {
* field here, NOT name. We're NOT always guaranteed to have a name
* for a connection, but the number should be displayable.
*/
- @UnsupportedAppUsage
- public String name;
- @UnsupportedAppUsage
- public String phoneNumber;
+ private String name;
+ private String phoneNumber;
+ /** @hide */
public String normalizedNumber;
+ /** @hide */
public String geoDescription;
-
+ /** @hide */
public String cnapName;
+ /** @hide */
public int numberPresentation;
+ /** @hide */
public int namePresentation;
+ /** @hide */
public boolean contactExists;
-
+ /** @hide */
public String phoneLabel;
- /* Split up the phoneLabel into number type and label name */
+ /**
+ * Split up the phoneLabel into number type and label name.
+ * @hide
+ */
@UnsupportedAppUsage
public int numberType;
+ /** @hide */
@UnsupportedAppUsage
public String numberLabel;
-
+ /** @hide */
public int photoResource;
// Contact ID, which will be 0 if a contact comes from the corp CP2.
- @UnsupportedAppUsage
- public long contactIdOrZero;
+ private long contactIdOrZero;
+ /** @hide */
public boolean needUpdate;
+ /** @hide */
public Uri contactRefUri;
+ /** @hide */
public String lookupKey;
-
+ /** @hide */
public ComponentName preferredPhoneAccountComponent;
+ /** @hide */
public String preferredPhoneAccountId;
-
+ /** @hide */
public long userType;
/**
* Contact display photo URI. If a contact has no display photo but a thumbnail, it'll be
* the thumbnail URI instead.
*/
- public Uri contactDisplayPhotoUri;
+ private Uri contactDisplayPhotoUri;
// fields to hold individual contact preference data,
// including the send to voicemail flag and the ringtone
// uri reference.
+ /** @hide */
public Uri contactRingtoneUri;
+ /** @hide */
public boolean shouldSendToVoicemail;
/**
@@ -141,6 +155,8 @@ public class CallerInfo {
*
* The {@link #isCachedPhotoCurrent} flag indicates if the image
* data needs to be reloaded.
+ *
+ * @hide
*/
public Drawable cachedPhoto;
/**
@@ -153,18 +169,23 @@ public class CallerInfo {
*
* The {@link #isCachedPhotoCurrent} flag indicates if the image
* data needs to be reloaded.
+ *
+ * @hide
*/
public Bitmap cachedPhotoIcon;
/**
* Boolean which indicates if {@link #cachedPhoto} and
* {@link #cachedPhotoIcon} is fresh enough. If it is false,
* those images aren't pointing to valid objects.
+ *
+ * @hide
*/
public boolean isCachedPhotoCurrent;
private boolean mIsEmergency;
private boolean mIsVoiceMail;
+ /** @hide */
@UnsupportedAppUsage
public CallerInfo() {
// TODO: Move all the basic initialization here?
@@ -180,6 +201,8 @@ public class CallerInfo {
* @param cursor the first object in the cursor is used to build the CallerInfo object.
* @return the CallerInfo which contains the caller id for the given
* number. The returned CallerInfo is null if no number is supplied.
+ *
+ * @hide
*/
public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
CallerInfo info = new CallerInfo();
@@ -321,6 +344,8 @@ public class CallerInfo {
* @param contactRef the URI used to lookup caller id
* @return the CallerInfo which contains the caller id for the given
* number. The returned CallerInfo is null if no number is supplied.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static CallerInfo getCallerInfo(Context context, Uri contactRef) {
@@ -346,6 +371,8 @@ public class CallerInfo {
* number. The returned CallerInfo is null if no number is supplied. If
* a matching number is not found, then a generic caller info is returned,
* with all relevant fields empty or null.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static CallerInfo getCallerInfo(Context context, String number) {
@@ -365,6 +392,8 @@ public class CallerInfo {
* number. The returned CallerInfo is null if no number is supplied. If
* a matching number is not found, then a generic caller info is returned,
* with all relevant fields empty or null.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static CallerInfo getCallerInfo(Context context, String number, int subId) {
@@ -398,6 +427,59 @@ public class CallerInfo {
}
/**
+ * @return Name assocaited with this caller.
+ */
+ @Nullable
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set caller Info Name.
+ * @param name caller Info Name
+ *
+ * @hide
+ */
+ public void setName(@Nullable String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return Phone number assocaited with this caller.
+ */
+ @Nullable
+ public String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ /** @hide */
+ public void setPhoneNumber(String number) {
+ phoneNumber = number;
+ }
+
+ /**
+ * @return Contact ID, which will be 0 if a contact comes from the corp Contacts Provider.
+ */
+ public long getContactId() {
+ return contactIdOrZero;
+ }
+
+ /**
+ * @return Contact display photo URI. If a contact has no display photo but a thumbnail,
+ * it'll the thumbnail URI instead.
+ */
+ @Nullable
+ public Uri getContactDisplayPhotoUri() {
+ return contactDisplayPhotoUri;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void SetContactDisplayPhotoUri(Uri photoUri) {
+ contactDisplayPhotoUri = photoUri;
+ }
+
+ /**
* Performs another lookup if previous lookup fails and it's a SIP call
* and the peer's username is all numeric. Look up the username as it
* could be a PSTN number in the contact database.
@@ -425,6 +507,7 @@ public class CallerInfo {
/**
* @return true if the caller info is an emergency number.
+ * @hide
*/
public boolean isEmergencyNumber() {
return mIsEmergency;
@@ -432,6 +515,7 @@ public class CallerInfo {
/**
* @return true if the caller info is a voicemail number.
+ * @hide
*/
public boolean isVoiceMailNumber() {
return mIsVoiceMail;
@@ -591,6 +675,7 @@ public class CallerInfo {
* @param context the context used to look up the current locale / country
* @param fallbackNumber if this CallerInfo's phoneNumber field is empty,
* this specifies a fallback number to use instead.
+ * @hide
*/
public void updateGeoDescription(Context context, String fallbackNumber) {
String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber;
@@ -600,6 +685,8 @@ public class CallerInfo {
/**
* @return a geographical description string for the specified number.
* @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
+ *
+ * @hide
*/
public static String getGeoDescription(Context context, String number) {
if (VDBG) Rlog.v(TAG, "getGeoDescription('" + number + "')...");
@@ -657,6 +744,7 @@ public class CallerInfo {
return countryIso;
}
+ /** @hide */
protected static String getCurrentCountryIso(Context context) {
return getCurrentCountryIso(context, Locale.getDefault());
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/android/telephony/CallerInfoAsyncQuery.java
index 4e1ff8f5b7cd..88b471efa3c8 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/android/telephony/CallerInfoAsyncQuery.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.telephony;
import android.app.ActivityManager;
import android.content.AsyncQueryHandler;
@@ -31,9 +31,6 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract.PhoneLookup;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import dalvik.annotation.compat.UnsupportedAppUsage;
@@ -346,16 +343,16 @@ public class CallerInfoAsyncQuery {
// Use the number entered by the user for display.
if (!TextUtils.isEmpty(cw.number)) {
- mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number,
+ mCallerInfo.setPhoneNumber(PhoneNumberUtils.formatNumber(cw.number,
mCallerInfo.normalizedNumber,
- CallerInfo.getCurrentCountryIso(mContext));
+ CallerInfo.getCurrentCountryIso(mContext)));
}
// This condition refer to the google default code for geo.
// If the number exists in Contacts, the CallCard would never show
// the geo description, so it would be unnecessary to query it.
if (ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION) {
- if (TextUtils.isEmpty(mCallerInfo.name)) {
+ if (TextUtils.isEmpty(mCallerInfo.getName())) {
if (DBG) Rlog.d(LOG_TAG, "start querying geo description");
cw.event = EVENT_GET_GEO_DESCRIPTION;
startQuery(token, cw, null, null, null, null, null);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2aca206a8cc5..b4495787bb80 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -108,6 +108,19 @@ public class CarrierConfigManager {
"call_forwarding_visibility_bool";
/**
+ * Boolean indicating if carrier supports call forwarding option "When unreachable".
+ *
+ * {@code true}: Call forwarding option "When unreachable" is supported.
+ * {@code false}: Call forwarding option "When unreachable" is not supported. Option will be
+ * greyed out in the UI.
+ *
+ * By default this value is true.
+ * @hide
+ */
+ public static final String KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL =
+ "call_forwarding_when_unreachable_supported_bool";
+
+ /**
* Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu.
* true means visible. false means gone.
* @hide
@@ -2791,6 +2804,14 @@ public class CarrierConfigManager {
"opportunistic_network_data_switch_exit_hysteresis_time_long";
/**
+ * Controls whether to do ping test before switching data to opportunistic network.
+ * This carrier config is used to disable this feature.
+ * @hide
+ */
+ public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL =
+ "ping_test_before_data_switch_bool";
+
+ /**
* Controls time in milli seconds until DcTracker reevaluates 5G connection state.
* @hide
*/
@@ -3242,6 +3263,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
+ sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
@@ -3572,6 +3594,7 @@ public class CarrierConfigManager {
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000);
/* Default value is 3 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000);
+ sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true);
/* Default value is 1 hour. */
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putAll(Gps.getDefaults());
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index a0af392fc6d5..43bc85c9f2bd 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -16,43 +16,65 @@
package android.telephony;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Range;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
- * Reports modem activity information
+ * Reports modem activity information.
* @hide
*/
-public class ModemActivityInfo implements Parcelable {
+@SystemApi
+public final class ModemActivityInfo implements Parcelable {
/**
- * Tx power index
- * index 0 = tx_power < 0dBm
- * index 1 = 0dBm < tx_power < 5dBm
- * index 2 = 5dBm < tx_power < 15dBm
- * index 3 = 15dBm < tx_power < 20dBm
- * index 4 = tx_power > 20dBm
+ * Tx(transmit) power level. see power index below
+ * <ul>
+ * <li> index 0 = tx_power < 0dBm. </li>
+ * <li> index 1 = 0dBm < tx_power < 5dBm. </li>
+ * <li> index 2 = 5dBm < tx_power < 15dBm. </li>
+ * <li> index 3 = 15dBm < tx_power < 20dBm. </li>
+ * <li> index 4 = tx_power > 20dBm. </li>
+ * </ul>
*/
public static final int TX_POWER_LEVELS = 5;
+ private static final Range<Integer>[] TX_POWER_RANGES = new Range[] {
+ new Range<>(Integer.MIN_VALUE, 0),
+ new Range<>(0, 5),
+ new Range<>(5, 15),
+ new Range<>(15, 20),
+ new Range<>(20, Integer.MAX_VALUE)
+
+ };
private long mTimestamp;
private int mSleepTimeMs;
private int mIdleTimeMs;
- private int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+ private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS);
private int mRxTimeMs;
- private int mEnergyUsed;
public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
- int[] txTimeMs, int rxTimeMs, int energyUsed) {
+ @NonNull int[] txTimeMs, int rxTimeMs) {
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
if (txTimeMs != null) {
- System.arraycopy(txTimeMs, 0, mTxTimeMs, 0, Math.min(txTimeMs.length, TX_POWER_LEVELS));
+ populateTransmitPowerRange(txTimeMs);
}
mRxTimeMs = rxTimeMs;
- mEnergyUsed = energyUsed;
+ }
+
+ /** helper API to populate tx power range for each bucket **/
+ private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
+ for (int i = 0; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
+ mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
+ }
}
@Override
@@ -61,9 +83,8 @@ public class ModemActivityInfo implements Parcelable {
+ " mTimestamp=" + mTimestamp
+ " mSleepTimeMs=" + mSleepTimeMs
+ " mIdleTimeMs=" + mIdleTimeMs
- + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString()
+ " mRxTimeMs=" + mRxTimeMs
- + " mEnergyUsed=" + mEnergyUsed
+ "}";
}
@@ -82,9 +103,8 @@ public class ModemActivityInfo implements Parcelable {
txTimeMs[i] = in.readInt();
}
int rxTimeMs = in.readInt();
- int energyUsed = in.readInt();
return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
- txTimeMs, rxTimeMs, energyUsed);
+ txTimeMs, rxTimeMs);
}
public ModemActivityInfo[] newArray(int size) {
@@ -97,102 +117,153 @@ public class ModemActivityInfo implements Parcelable {
dest.writeInt(mSleepTimeMs);
dest.writeInt(mIdleTimeMs);
for (int i = 0; i < TX_POWER_LEVELS; i++) {
- dest.writeInt(mTxTimeMs[i]);
+ dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis());
}
dest.writeInt(mRxTimeMs);
- dest.writeInt(mEnergyUsed);
}
/**
- * @return timestamp of record creation
+ * @return milliseconds since boot, including mTimeInMillis spent in sleep.
+ * @see SystemClock#elapsedRealtime()
*/
public long getTimestamp() {
return mTimestamp;
}
+ /** @hide */
public void setTimestamp(long timestamp) {
mTimestamp = timestamp;
}
/**
- * @return tx time in ms. It's an array of tx times
- * with each index...
+ * @return an arrayList of {@link TransmitPower} with each element representing the total time where
+ * transmitter is awake time (in ms) for a given power range (in dbm).
+ *
+ * @see #TX_POWER_LEVELS
*/
- public int [] getTxTimeMillis() {
- return mTxTimeMs;
+ @NonNull
+ public List<TransmitPower> getTransmitPowerInfo() {
+ return mTransmitPowerInfo;
}
- public void setTxTimeMillis(int[] txTimeMs) {
- mTxTimeMs = txTimeMs;
+ /** @hide */
+ public void setTransmitTimeMillis(int[] txTimeMs) {
+ populateTransmitPowerRange(txTimeMs);
+ }
+
+ /** @hide */
+ @NonNull
+ public int[] getTransmitTimeMillis() {
+ int[] transmitTimeMillis = new int[TX_POWER_LEVELS];
+ for (int i = 0; i < transmitTimeMillis.length; i++) {
+ transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis();
+ }
+ return transmitTimeMillis;
}
/**
- * @return sleep time in ms.
+ * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state.
*/
public int getSleepTimeMillis() {
return mSleepTimeMs;
}
+ /** @hide */
public void setSleepTimeMillis(int sleepTimeMillis) {
mSleepTimeMs = sleepTimeMillis;
}
/**
- * @return idle time in ms.
+ * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are
+ * active.
*/
public int getIdleTimeMillis() {
return mIdleTimeMs;
}
+ /** @hide */
public void setIdleTimeMillis(int idleTimeMillis) {
mIdleTimeMs = idleTimeMillis;
}
/**
- * @return rx time in ms.
+ * @return rx(receive) mTimeInMillis in ms.
*/
- public int getRxTimeMillis() {
+ public int getReceiveTimeMillis() {
return mRxTimeMs;
}
- public void setRxTimeMillis(int rxTimeMillis) {
+ /** @hide */
+ public void setReceiveTimeMillis(int rxTimeMillis) {
mRxTimeMs = rxTimeMillis;
}
/**
- * product of current(mA), voltage(V) and time(ms)
- * @return energy used
- */
- public int getEnergyUsed () {
- return mEnergyUsed;
- }
-
- public void setEnergyUsed(int energyUsed) {
- mEnergyUsed = energyUsed;
- }
-
- /**
- * @return if the record is valid
+ * @return {@code true} if this {@link ModemActivityInfo} record is valid,
+ * {@code false} otherwise.
+ *
+ * @hide
*/
public boolean isValid() {
- for (int txVal : getTxTimeMillis()) {
- if(txVal < 0) {
+ for (TransmitPower powerInfo : getTransmitPowerInfo()) {
+ if(powerInfo.getTimeInMillis() < 0) {
return false;
}
}
return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
- && (getRxTimeMillis() >= 0) && (getEnergyUsed() >= 0) && !isEmpty());
+ && (getReceiveTimeMillis() >= 0) && !isEmpty());
}
private boolean isEmpty() {
- for (int txVal : getTxTimeMillis()) {
- if(txVal != 0) {
+ for (TransmitPower txVal : getTransmitPowerInfo()) {
+ if(txVal.getTimeInMillis() != 0) {
return false;
}
}
return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
- && (getRxTimeMillis() == 0) && (getEnergyUsed() == 0));
+ && (getReceiveTimeMillis() == 0));
+ }
+
+ /**
+ * Transmit power Information, including the power range in dbm and the total time (in ms) where
+ * the transmitter is active/awake for this power range.
+ * e.g, range: 0dbm(lower) ~ 5dbm(upper)
+ * time: 5ms
+ */
+ public class TransmitPower {
+ private int mTimeInMillis;
+ private Range<Integer> mPowerRangeInDbm;
+ /** @hide */
+ public TransmitPower(@NonNull Range<Integer> range, int time) {
+ this.mTimeInMillis = time;
+ this.mPowerRangeInDbm = range;
+ }
+
+ /**
+ * @return the total time in ms where the transmitter is active/wake for this power range
+ * {@link #getPowerRangeInDbm()}.
+ */
+ public int getTimeInMillis() {
+ return mTimeInMillis;
+ }
+
+ /**
+ * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper)
+ */
+ @NonNull
+ public Range<Integer> getPowerRangeInDbm() {
+ return mPowerRangeInDbm;
+ }
+
+ @Override
+ public String toString() {
+ return "TransmitPower{"
+ + " mTimeInMillis=" + mTimeInMillis
+ + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower()
+ + "," + mPowerRangeInDbm.getUpper()
+ + "}}";
+ }
}
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 6a3c06eb622d..1ffed2543ee4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -357,6 +357,30 @@ public class PhoneStateListener {
@SystemApi
public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000;
+ /**
+ * Listen for the emergency number placed from an outgoing call.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+ *
+ * @see #onOutgoingEmergencyCall
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 0x10000000;
+
+ /**
+ * Listen for the emergency number placed from an outgoing SMS.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+ *
+ * @see #onOutgoingEmergencySms
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 0x20000000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -839,6 +863,27 @@ public class PhoneStateListener {
}
/**
+ * Callback invoked when an outgoing call is placed to an emergency number.
+ *
+ * @param placedEmergencyNumber the emergency number {@link EmergencyNumber} the call is placed
+ * to.
+ * @hide
+ */
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when an outgoing SMS is placed to an emergency number.
+ *
+ * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
+ * @hide
+ */
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when OEM hook raw event is received on the registered subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
* PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
@@ -1146,7 +1191,6 @@ public class PhoneStateListener {
() -> psl.onPhysicalChannelConfigurationChanged(configs)));
}
- @Override
public void onEmergencyNumberListChanged(Map emergencyNumberList) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
@@ -1156,6 +1200,24 @@ public class PhoneStateListener {
() -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
}
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber)));
+ }
+
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onOutgoingEmergencySms(sentEmergencyNumber)));
+ }
+
public void onPhoneCapabilityChanged(PhoneCapability capability) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
index 606df15b1782..6a35e33f44f9 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
@@ -22,7 +22,6 @@ package android.telephony.ims.aidl;
*/
oneway interface IImsSmsListener {
void onSendSmsResult(int token, int messageRef, int status, int reason);
- void onSmsStatusReportReceived(int token, int messageRef, in String format,
- in byte[] pdu);
+ void onSmsStatusReportReceived(int token, in String format, in byte[] pdu);
void onSmsReceived(int token, in String format, in byte[] pdu);
-} \ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 852c8e0618c8..2e4bfb3149be 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -147,7 +147,7 @@ public class ImsSmsImplBase {
* {@link SmsMessage#FORMAT_3GPP2}.
* @param smsc the Short Message Service Center address.
* @param isRetry whether it is a retry of an already attempted message or not.
- * @param pdu PDUs representing the contents of the message.
+ * @param pdu PDU representing the contents of the message.
*/
public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
byte[] pdu) {
@@ -166,27 +166,29 @@ public class ImsSmsImplBase {
* provider.
*
* @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+ * @param messageRef the message reference
* @param result result of delivering the message. Valid values are:
* {@link #DELIVER_STATUS_OK},
* {@link #DELIVER_STATUS_ERROR_GENERIC},
* {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
* {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
- * @param messageRef the message reference
*/
- public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
+ public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
}
/**
* This method will be triggered by the platform after
- * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
+ * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
+ * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
* result to the IMS provider.
*
- * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
+ * or {@link #onSmsStatusReportReceived(int, String, byte[])}
+ * @param messageRef the message reference
* @param result result of delivering the message. Valid values are:
* {@link #STATUS_REPORT_STATUS_OK},
* {@link #STATUS_REPORT_STATUS_ERROR}
- * @param messageRef the message reference
*/
public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
@@ -204,7 +206,7 @@ public class ImsSmsImplBase {
* callbacks for this message.
* @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
* {@link SmsMessage#FORMAT_3GPP2}.
- * @param pdu PDUs representing the contents of the message.
+ * @param pdu PDU representing the contents of the message.
* @throws RuntimeException if called before {@link #onReady()} is triggered.
*/
public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
@@ -285,23 +287,32 @@ public class ImsSmsImplBase {
}
/**
- * Sets the status report of the sent message.
+ * This method should be triggered by the IMS providers when the status report of the sent
+ * message is received. The platform will handle the report and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
*
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+ * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param messageRef the message reference.
* @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
- * @param pdu PDUs representing the content of the status report.
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDU representing the content of the status report.
* @throws RuntimeException if called before {@link #onReady()} is triggered
+ *
+ * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the
+ * message reference.
*/
+ @Deprecated
public final void onSmsStatusReportReceived(int token, int messageRef, String format,
- byte[] pdu) throws RuntimeException{
+ byte[] pdu) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
}
try {
- mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
+ mListener.onSmsStatusReportReceived(token, format, pdu);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
@@ -310,6 +321,46 @@ public class ImsSmsImplBase {
}
/**
+ * This method should be triggered by the IMS providers when the status report of the sent
+ * message is received. The platform will handle the report and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
+ *
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+ * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
+ * @param token unique token generated by IMS providers that the platform will use to trigger
+ * callbacks for this message.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDU representing the content of the status report.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered
+ */
+ public final void onSmsStatusReportReceived(int token, String format, byte[] pdu)
+ throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsStatusReportReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSmsReport(
+ token,
+ message.mWrappedSmsMessage.mMessageRef,
+ STATUS_REPORT_STATUS_ERROR);
+ } else {
+ Log.w(LOG_TAG,
+ "onSmsStatusReportReceivedWithoutMessageRef: Invalid pdu entered.");
+ acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
+ }
+ }
+ }
+ }
+
+ /**
* Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
* Provider.
*
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 4a263f060ca5..90019eef62fd 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -58,6 +58,8 @@ oneway interface IPhoneStateListener {
void onRadioPowerStateChanged(in int state);
void onCallAttributesChanged(in CallAttributes callAttributes);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
+ void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber);
+ void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
}
diff --git a/tests/BootImageProfileTest/Android.bp b/tests/BootImageProfileTest/Android.bp
new file mode 100644
index 000000000000..1b097a8af0f9
--- /dev/null
+++ b/tests/BootImageProfileTest/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+java_test_host {
+ name: "BootImageProfileTest",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed"],
+ test_suites: ["general-tests"],
+}
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
new file mode 100644
index 000000000000..c13200778c4b
--- /dev/null
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<configuration description="Config for BootImageProfileTest">
+ <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
+ furthermore the changes in /data/local.prop don't actually seem to get picked up.
+ -->
+ <target_preparer
+ class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
+ <option name="force-skip-system-props" value="true" />
+
+ <option name="run-command" value="setprop dalvik.vm.profilesystemserver true" />
+ <option name="run-command" value="setprop dalvik.vm.profilebootclasspath true" />
+
+ <!-- Profiling does not pick up the above changes we restart the shell -->
+ <option name="run-command" value="stop" />
+ <option name="run-command" value="start" />
+
+ <!-- give it some time to restart the shell; otherwise the first unit test might fail -->
+ <option name="run-command" value="sleep 2" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.bootimageprofile.BootImageProfileTest" />
+ </test>
+</configuration>
diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING
new file mode 100644
index 000000000000..1b569f9455bf
--- /dev/null
+++ b/tests/BootImageProfileTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "BootImageProfileTest"
+ }
+ ]
+}
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
new file mode 100644
index 000000000000..17986a3c9d61
--- /dev/null
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.bootimageprofile;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class BootImageProfileTest implements IDeviceTest {
+ private ITestDevice mTestDevice;
+ private static final String SYSTEM_SERVER_PROFILE =
+ "/data/misc/profiles/cur/0/android/primary.prof";
+
+ @Override
+ public void setDevice(ITestDevice testDevice) {
+ mTestDevice = testDevice;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mTestDevice;
+ }
+
+ /**
+ * Test that the boot image profile properties are set.
+ */
+ @Test
+ public void testProperties() throws Exception {
+ String res = mTestDevice.getProperty("dalvik.vm.profilebootclasspath");
+ assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
+ res = mTestDevice.getProperty("dalvik.vm.profilesystemserver");
+ assertTrue("profile system server not enabled", res != null && res.equals("true"));
+ }
+
+ private void forceSaveProfile(String pkg) throws Exception {
+ String pid = mTestDevice.executeShellCommand("pidof " + pkg).trim();
+ assertTrue("Invalid pid " + pid, pid.length() > 0);
+ String res = mTestDevice.executeShellCommand("kill -s SIGUSR1 " + pid).trim();
+ assertTrue("kill SIGUSR1: " + res, res.length() == 0);
+ }
+
+ @Test
+ public void testSystemServerProfile() throws Exception {
+ // Trunacte the profile before force it to be saved to prevent previous profiles
+ // causing the test to pass.
+ String res;
+ res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
+ assertTrue(res, res.length() == 0);
+ // Force save profiles in case the system just started.
+ Thread.sleep(1000);
+ forceSaveProfile("system_server");
+ Thread.sleep(2000);
+ // Validate that the profile is non empty.
+ res = mTestDevice.executeShellCommand("profman --dump-only --profile-file="
+ + SYSTEM_SERVER_PROFILE);
+ boolean sawFramework = false;
+ boolean sawServices = false;
+ for (String line : res.split("\n")) {
+ if (line.contains("framework.jar")) {
+ sawFramework = true;
+ } else if (line.contains("services.jar")) {
+ sawServices = true;
+ }
+ }
+ assertTrue("Did not see framework.jar in " + res, sawFramework);
+ assertTrue("Did not see services.jar in " + res, sawServices);
+ }
+}
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 30bb3ef5456b..f0c5baad222b 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable {
- // Code below generated by codegen v1.0.0.
+ // Code below generated by codegen v1.0.1.
//
// DO NOT MODIFY!
//
@@ -1798,8 +1798,8 @@ public final class SampleDataClass implements Parcelable {
}
@DataClass.Generated(
- time = 1565126122525L,
- codegenVersion = "1.0.0",
+ time = 1568235365376L,
+ codegenVersion = "1.0.1",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index 11f03a72c051..86f37fe1057e 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -74,7 +74,7 @@ public class SampleWithCustomBuilder {
- // Code below generated by codegen v1.0.0.
+ // Code below generated by codegen v1.0.1.
//
// DO NOT MODIFY!
//
@@ -176,8 +176,8 @@ public class SampleWithCustomBuilder {
}
@DataClass.Generated(
- time = 1565126123496L,
- codegenVersion = "1.0.0",
+ time = 1568235366386L,
+ codegenVersion = "1.0.1",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp
index 5d8ed2c205e9..e0f0188ee618 100644
--- a/tests/FlickerTests/lib/Android.bp
+++ b/tests/FlickerTests/lib/Android.bp
@@ -30,10 +30,23 @@ java_test {
}
java_library {
+ name: "flickerlib_without_helpers",
+ platform_apis: true,
+ srcs: ["src/**/*.java"],
+ exclude_srcs: ["src/**/helpers/*.java"],
+ static_libs: [
+ "cts-wm-util",
+ "platformprotosnano",
+ "layersprotosnano",
+ "truth-prebuilt"
+ ],
+}
+
+java_library {
name: "flickerautomationhelperlib",
sdk_version: "test_current",
srcs: [
- "src/com/android/server/wm/flicker/AutomationUtils.java",
+ "src/com/android/server/wm/flicker/helpers/AutomationUtils.java",
"src/com/android/server/wm/flicker/WindowUtils.java",
],
static_libs: [
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
index 84f9f871324c..38255ee6fe8d 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
@@ -24,14 +24,14 @@ import java.util.function.Function;
* results. Assertions are functions that are applied over a single trace entry and returns a
* result which includes a detailed reason if the assertion fails.
*/
-class Assertions {
+public class Assertions {
/**
* Checks assertion on a single trace entry.
*
* @param <T> trace entry type to perform the assertion on.
*/
@FunctionalInterface
- interface TraceAssertion<T> extends Function<T, Result> {
+ public interface TraceAssertion<T> extends Function<T, Result> {
/**
* Returns an assertion that represents the logical negation of this assertion.
*
@@ -46,7 +46,7 @@ class Assertions {
* Checks assertion on a single layers trace entry.
*/
@FunctionalInterface
- interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
+ public interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
}
@@ -54,11 +54,11 @@ class Assertions {
* Utility class to store assertions with an identifier to help generate more useful debug
* data when dealing with multiple assertions.
*/
- static class NamedAssertion<T> {
- final TraceAssertion<T> assertion;
- final String name;
+ public static class NamedAssertion<T> {
+ public final TraceAssertion<T> assertion;
+ public final String name;
- NamedAssertion(TraceAssertion<T> assertion, String name) {
+ public NamedAssertion(TraceAssertion<T> assertion, String name) {
this.assertion = assertion;
this.name = name;
}
@@ -67,21 +67,21 @@ class Assertions {
/**
* Contains the result of an assertion including the reason for failed assertions.
*/
- static class Result {
- static final String NEGATION_PREFIX = "!";
- final boolean success;
- final long timestamp;
- final String assertionName;
- final String reason;
-
- Result(boolean success, long timestamp, String assertionName, String reason) {
+ public static class Result {
+ public static final String NEGATION_PREFIX = "!";
+ public final boolean success;
+ public final long timestamp;
+ public final String assertionName;
+ public final String reason;
+
+ public Result(boolean success, long timestamp, String assertionName, String reason) {
this.success = success;
this.timestamp = timestamp;
this.assertionName = assertionName;
this.reason = reason;
}
- Result(boolean success, String reason) {
+ public Result(boolean success, String reason) {
this.success = success;
this.reason = reason;
this.assertionName = "";
@@ -91,7 +91,7 @@ class Assertions {
/**
* Returns the negated {@code Result} and adds a negation prefix to the assertion name.
*/
- Result negate() {
+ public Result negate() {
String negatedAssertionName;
if (this.assertionName.startsWith(NEGATION_PREFIX)) {
negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
@@ -101,11 +101,11 @@ class Assertions {
return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
}
- boolean passed() {
+ public boolean passed() {
return this.success;
}
- boolean failed() {
+ public boolean failed() {
return !this.success;
}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
index 3c65d3c341b3..5c4df81299c1 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
@@ -38,11 +38,11 @@ public class AssertionsChecker<T extends ITraceEntry> {
private AssertionOption mOption = AssertionOption.NONE;
private List<NamedAssertion<T>> mAssertions = new LinkedList<>();
- void add(Assertions.TraceAssertion<T> assertion, String name) {
+ public void add(Assertions.TraceAssertion<T> assertion, String name) {
mAssertions.add(new NamedAssertion<>(assertion, name));
}
- void filterByRange(long startTime, long endTime) {
+ public void filterByRange(long startTime, long endTime) {
mFilterEntriesByRange = true;
mFilterStartTime = startTime;
mFilterEndTime = endTime;
@@ -75,7 +75,7 @@ public class AssertionsChecker<T extends ITraceEntry> {
* @param entries list of entries to perform assertions on
* @return list of failed assertion results
*/
- List<Result> test(List<T> entries) {
+ public List<Result> test(List<T> entries) {
List<T> filteredEntries;
List<Result> failures;
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
index 9525f41b46b2..c47f7f42e54e 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker;
/**
* Common interface for Layer and WindowManager trace entries.
*/
-interface ITraceEntry {
+public interface ITraceEntry {
/**
* @return timestamp of current entry
*/
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
index 660ec0fe4833..68986d48783a 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker;
-import android.annotation.Nullable;
import android.graphics.Rect;
import android.surfaceflinger.nano.Layers.LayerProto;
import android.surfaceflinger.nano.Layers.RectProto;
@@ -25,11 +24,14 @@ import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
import android.util.SparseArray;
+import androidx.annotation.Nullable;
+
import com.android.server.wm.flicker.Assertions.Result;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -57,7 +59,7 @@ public class LayersTrace {
* @param data binary proto data
* @param source Path to source of data for additional debug information
*/
- static LayersTrace parseFrom(byte[] data, Path source) {
+ public static LayersTrace parseFrom(byte[] data, Path source) {
List<Entry> entries = new ArrayList<>();
LayersTraceFileProto fileProto;
try {
@@ -79,15 +81,15 @@ public class LayersTrace {
*
* @param data binary proto data
*/
- static LayersTrace parseFrom(byte[] data) {
+ public static LayersTrace parseFrom(byte[] data) {
return parseFrom(data, null);
}
- List<Entry> getEntries() {
+ public List<Entry> getEntries() {
return mEntries;
}
- Entry getEntry(long timestamp) {
+ public Entry getEntry(long timestamp) {
Optional<Entry> entry = mEntries.stream()
.filter(e -> e.getTimestamp() == timestamp)
.findFirst();
@@ -97,14 +99,14 @@ public class LayersTrace {
return entry.get();
}
- Optional<Path> getSource() {
+ public Optional<Path> getSource() {
return Optional.ofNullable(mSource);
}
/**
* Represents a single Layer trace entry.
*/
- static class Entry implements ITraceEntry {
+ public static class Entry implements ITraceEntry {
private long mTimestamp;
private List<Layer> mRootLayers; // hierarchical representation of layers
private List<Layer> mFlattenedLayers = null;
@@ -117,7 +119,7 @@ public class LayersTrace {
/**
* Constructs the layer hierarchy from a flattened list of layers.
*/
- static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
+ public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
SparseArray<Layer> layerMap = new SparseArray<>();
ArrayList<Layer> orphans = new ArrayList<>();
for (LayerProto proto : protos) {
@@ -181,7 +183,7 @@ public class LayersTrace {
/**
* Checks if a region specified by {@code testRect} is covered by all visible layers.
*/
- Result coversRegion(Rect testRect) {
+ public Result coversRegion(Rect testRect) {
String assertionName = "coversRegion";
Collection<Layer> layers = asFlattenedLayers();
@@ -224,7 +226,7 @@ public class LayersTrace {
* Checks if a layer with name {@code layerName} has a visible region
* {@code expectedVisibleRegion}.
*/
- Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
+ public Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
String assertionName = "hasVisibleRegion";
String reason = "Could not find " + layerName;
for (Layer layer : asFlattenedLayers()) {
@@ -252,7 +254,7 @@ public class LayersTrace {
/**
* Checks if a layer with name {@code layerName} is visible.
*/
- Result isVisible(String layerName) {
+ public Result isVisible(String layerName) {
String assertionName = "isVisible";
String reason = "Could not find " + layerName;
for (Layer layer : asFlattenedLayers()) {
@@ -277,24 +279,27 @@ public class LayersTrace {
return mTimestamp;
}
- List<Layer> getRootLayers() {
+ public List<Layer> getRootLayers() {
return mRootLayers;
}
- List<Layer> asFlattenedLayers() {
+ /**
+ * Returns all layers as a flattened list using a depth first traversal.
+ */
+ public List<Layer> asFlattenedLayers() {
if (mFlattenedLayers == null) {
- mFlattenedLayers = new ArrayList<>();
+ mFlattenedLayers = new LinkedList<>();
ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
while (!pendingLayers.isEmpty()) {
Layer layer = pendingLayers.remove(0);
mFlattenedLayers.add(layer);
- pendingLayers.addAll(layer.mChildren);
+ pendingLayers.addAll(0, layer.mChildren);
}
}
return mFlattenedLayers;
}
- Rect getVisibleBounds(String layerName) {
+ public Rect getVisibleBounds(String layerName) {
List<Layer> layers = asFlattenedLayers();
for (Layer layer : layers) {
if (layer.mProto.name.contains(layerName) && layer.isVisible()) {
@@ -308,12 +313,12 @@ public class LayersTrace {
/**
* Represents a single layer with links to its parent and child layers.
*/
- static class Layer {
+ public static class Layer {
@Nullable
- LayerProto mProto;
- List<Layer> mChildren;
+ public LayerProto mProto;
+ public List<Layer> mChildren;
@Nullable
- Layer mParent = null;
+ public Layer mParent = null;
private Layer(LayerProto proto) {
this.mProto = proto;
@@ -328,16 +333,16 @@ public class LayersTrace {
this.mParent = parentLayer;
}
- int getId() {
+ public int getId() {
return mProto.id;
}
- boolean isActiveBufferEmpty() {
+ public boolean isActiveBufferEmpty() {
return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
|| this.mProto.activeBuffer.width == 0;
}
- boolean isVisibleRegionEmpty() {
+ public boolean isVisibleRegionEmpty() {
if (this.mProto.visibleRegion == null) {
return true;
}
@@ -345,32 +350,35 @@ public class LayersTrace {
return visibleRect.height() == 0 || visibleRect.width() == 0;
}
- boolean isHidden() {
+ public boolean isHidden() {
return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
}
- boolean isVisible() {
- return (!isActiveBufferEmpty() || isColorLayer()) &&
- !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty();
+ public boolean isVisible() {
+ return (!isActiveBufferEmpty() || isColorLayer())
+ && !isHidden()
+ && this.mProto.color != null
+ && this.mProto.color.a > 0
+ && !isVisibleRegionEmpty();
}
- boolean isColorLayer() {
+ public boolean isColorLayer() {
return this.mProto.type.equals("ColorLayer");
}
- boolean isRootLayer() {
+ public boolean isRootLayer() {
return mParent == null || mParent.mProto == null;
}
- boolean isInvisible() {
+ public boolean isInvisible() {
return !isVisible();
}
- boolean isHiddenByParent() {
+ public boolean isHiddenByParent() {
return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
}
- String getHiddenByParentReason() {
+ public String getHiddenByParentReason() {
String reason = "Layer " + mProto.name;
if (isHiddenByParent()) {
reason += " is hidden by parent: " + mParent.mProto.name;
@@ -380,7 +388,7 @@ public class LayersTrace {
return reason;
}
- String getVisibilityReason() {
+ public String getVisibilityReason() {
String reason = "Layer " + mProto.name;
if (isVisible()) {
reason += " is visible:";
@@ -399,7 +407,7 @@ public class LayersTrace {
if (isHidden()) {
reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)";
}
- if (this.mProto.color.a == 0) {
+ if (this.mProto.color == null || this.mProto.color.a == 0) {
reason += " color.a=0";
}
if (isVisibleRegionEmpty()) {
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
index 4085810a213d..4a5129ed2269 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
@@ -19,9 +19,10 @@ package com.android.server.wm.flicker;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.annotation.Nullable;
import android.graphics.Rect;
+import androidx.annotation.Nullable;
+
import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.flicker.LayersTrace.Entry;
import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
index 0a3fe3c00de2..241a1c04bdb8 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
@@ -16,10 +16,12 @@
package com.android.server.wm.flicker;
-import android.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
+import static com.android.server.wm.flicker.monitor.ITransitionMonitor.OUTPUT_DIR;
+
import android.util.Log;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.test.InstrumentationRegistry;
import com.android.server.wm.flicker.monitor.ITransitionMonitor;
@@ -89,7 +91,7 @@ import java.util.List;
* }
* </pre>
*/
-class TransitionRunner {
+public class TransitionRunner {
private static final String TAG = "FLICKER";
private final ScreenRecorder mScreenRecorder;
private final WindowManagerTraceMonitor mWmTraceMonitor;
@@ -128,8 +130,12 @@ class TransitionRunner {
mTestTag = builder.mTestTag;
}
- static TransitionBuilder newBuilder() {
- return new TransitionBuilder();
+ public static TransitionBuilder newBuilder() {
+ return newBuilder(OUTPUT_DIR.toString());
+ }
+
+ public static TransitionBuilder newBuilder(String outputDir) {
+ return new TransitionBuilder(outputDir);
}
/**
@@ -138,7 +144,7 @@ class TransitionRunner {
*
* @return itself
*/
- TransitionRunner run() {
+ public TransitionRunner run() {
mResults = new ArrayList<>();
mAllRunsMonitors.forEach(ITransitionMonitor::start);
mBeforeAlls.forEach(Runnable::run);
@@ -159,8 +165,7 @@ class TransitionRunner {
mAfterAlls.forEach(Runnable::run);
mAllRunsMonitors.forEach(monitor -> {
monitor.stop();
- Path path = monitor.save(mTestTag);
- Log.e(TAG, "Video saved to " + path.toString());
+ monitor.save(mTestTag);
});
return this;
}
@@ -170,7 +175,7 @@ class TransitionRunner {
*
* @return list of transition results.
*/
- List<TransitionResult> getResults() {
+ public List<TransitionResult> getResults() {
if (mResults == null) {
throw new IllegalStateException("Results do not exist!");
}
@@ -182,7 +187,7 @@ class TransitionRunner {
*
* @return list of transition results.
*/
- void deleteResults() {
+ public void deleteResults() {
if (mResults == null) {
return;
}
@@ -228,33 +233,33 @@ class TransitionRunner {
@VisibleForTesting
public static class TransitionResult {
@Nullable
- final Path layersTrace;
+ public final Path layersTrace;
@Nullable
- final Path windowManagerTrace;
+ public final Path windowManagerTrace;
@Nullable
- final Path screenCaptureVideo;
+ public final Path screenCaptureVideo;
private boolean flaggedForSaving;
- TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
+ public TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
@Nullable Path screenCaptureVideo) {
this.layersTrace = layersTrace;
this.windowManagerTrace = windowManagerTrace;
this.screenCaptureVideo = screenCaptureVideo;
}
- void flagForSaving() {
+ public void flagForSaving() {
flaggedForSaving = true;
}
- boolean canDelete() {
+ public boolean canDelete() {
return !flaggedForSaving;
}
- boolean layersTraceExists() {
+ public boolean layersTraceExists() {
return layersTrace != null && layersTrace.toFile().exists();
}
- byte[] getLayersTrace() {
+ public byte[] getLayersTrace() {
try {
return Files.toByteArray(this.layersTrace.toFile());
} catch (IOException e) {
@@ -262,11 +267,11 @@ class TransitionRunner {
}
}
- Path getLayersTracePath() {
+ public Path getLayersTracePath() {
return layersTrace;
}
- boolean windowManagerTraceExists() {
+ public boolean windowManagerTraceExists() {
return windowManagerTrace != null && windowManagerTrace.toFile().exists();
}
@@ -278,19 +283,19 @@ class TransitionRunner {
}
}
- Path getWindowManagerTracePath() {
+ public Path getWindowManagerTracePath() {
return windowManagerTrace;
}
- boolean screenCaptureVideoExists() {
+ public boolean screenCaptureVideoExists() {
return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
}
- Path screenCaptureVideoPath() {
+ public Path screenCaptureVideoPath() {
return screenCaptureVideo;
}
- void delete() {
+ public void delete() {
if (layersTraceExists()) layersTrace.toFile().delete();
if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
@@ -300,7 +305,7 @@ class TransitionRunner {
/**
* Builds a {@link TransitionRunner} instance.
*/
- static class TransitionBuilder {
+ public static class TransitionBuilder {
private ScreenRecorder mScreenRecorder;
private WindowManagerTraceMonitor mWmTraceMonitor;
private LayersTraceMonitor mLayersTraceMonitor;
@@ -323,15 +328,15 @@ class TransitionRunner {
private boolean mRecordAllRuns = false;
- TransitionBuilder() {
+ public TransitionBuilder(String outputDir) {
mScreenRecorder = new ScreenRecorder();
- mWmTraceMonitor = new WindowManagerTraceMonitor();
- mLayersTraceMonitor = new LayersTraceMonitor();
+ mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir);
+ mLayersTraceMonitor = new LayersTraceMonitor(outputDir);
mFrameStatsMonitor = new
WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
}
- TransitionRunner build() {
+ public TransitionRunner build() {
if (mCaptureWindowManagerTrace) {
mPerRunMonitors.add(mWmTraceMonitor);
}
@@ -355,52 +360,52 @@ class TransitionRunner {
return new TransitionRunner(this);
}
- TransitionBuilder runBeforeAll(Runnable runnable) {
+ public TransitionBuilder runBeforeAll(Runnable runnable) {
mBeforeAlls.add(runnable);
return this;
}
- TransitionBuilder runBefore(Runnable runnable) {
+ public TransitionBuilder runBefore(Runnable runnable) {
mBefores.add(runnable);
return this;
}
- TransitionBuilder run(Runnable runnable) {
+ public TransitionBuilder run(Runnable runnable) {
mTransitions.add(runnable);
return this;
}
- TransitionBuilder runAfter(Runnable runnable) {
+ public TransitionBuilder runAfter(Runnable runnable) {
mAfters.add(runnable);
return this;
}
- TransitionBuilder runAfterAll(Runnable runnable) {
+ public TransitionBuilder runAfterAll(Runnable runnable) {
mAfterAlls.add(runnable);
return this;
}
- TransitionBuilder repeat(int iterations) {
+ public TransitionBuilder repeat(int iterations) {
mIterations = iterations;
return this;
}
- TransitionBuilder skipWindowManagerTrace() {
+ public TransitionBuilder skipWindowManagerTrace() {
mCaptureWindowManagerTrace = false;
return this;
}
- TransitionBuilder skipLayersTrace() {
+ public TransitionBuilder skipLayersTrace() {
mCaptureLayersTrace = false;
return this;
}
- TransitionBuilder includeJankyRuns() {
+ public TransitionBuilder includeJankyRuns() {
mRunJankFree = false;
return this;
}
- TransitionBuilder recordEachRun() {
+ public TransitionBuilder recordEachRun() {
if (mRecordAllRuns) {
throw new IllegalArgumentException("Invalid option with recordAllRuns");
}
@@ -408,7 +413,7 @@ class TransitionRunner {
return this;
}
- TransitionBuilder recordAllRuns() {
+ public TransitionBuilder recordAllRuns() {
if (mRecordEachRun) {
throw new IllegalArgumentException("Invalid option with recordEachRun");
}
@@ -416,7 +421,11 @@ class TransitionRunner {
return this;
}
- TransitionBuilder withTag(String testTag) {
+ public TransitionBuilder withTag(String testTag) {
+ if (testTag.contains(" ")) {
+ throw new IllegalArgumentException("The test tag can not contain spaces since it "
+ + "is a part of the file name");
+ }
mTestTag = testTag;
return this;
}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
index e3592eb8cd01..412e72d82e55 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.nano.AppWindowTokenProto;
@@ -58,7 +58,7 @@ public class WindowManagerTrace {
* @param data binary proto data
* @param source Path to source of data for additional debug information
*/
- static WindowManagerTrace parseFrom(byte[] data, Path source) {
+ public static WindowManagerTrace parseFrom(byte[] data, Path source) {
List<Entry> entries = new ArrayList<>();
WindowManagerTraceFileProto fileProto;
@@ -73,7 +73,7 @@ public class WindowManagerTrace {
return new WindowManagerTrace(entries, source);
}
- static WindowManagerTrace parseFrom(byte[] data) {
+ public static WindowManagerTrace parseFrom(byte[] data) {
return parseFrom(data, null);
}
@@ -81,7 +81,7 @@ public class WindowManagerTrace {
return mEntries;
}
- Entry getEntry(long timestamp) {
+ public Entry getEntry(long timestamp) {
Optional<Entry> entry = mEntries.stream()
.filter(e -> e.getTimestamp() == timestamp)
.findFirst();
@@ -91,17 +91,17 @@ public class WindowManagerTrace {
return entry.get();
}
- Optional<Path> getSource() {
+ public Optional<Path> getSource() {
return Optional.ofNullable(mSource);
}
/**
* Represents a single WindowManager trace entry.
*/
- static class Entry implements ITraceEntry {
+ public static class Entry implements ITraceEntry {
private final WindowManagerTraceProto mProto;
- Entry(WindowManagerTraceProto proto) {
+ public Entry(WindowManagerTraceProto proto) {
mProto = proto;
}
@@ -162,7 +162,7 @@ public class WindowManagerTrace {
/**
* Checks if aboveAppWindow with {@code windowTitle} is visible.
*/
- Result isAboveAppWindowVisible(String windowTitle) {
+ public Result isAboveAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].aboveAppWindows;
@@ -173,7 +173,7 @@ public class WindowManagerTrace {
/**
* Checks if belowAppWindow with {@code windowTitle} is visible.
*/
- Result isBelowAppWindowVisible(String windowTitle) {
+ public Result isBelowAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].belowAppWindows;
@@ -185,7 +185,7 @@ public class WindowManagerTrace {
/**
* Checks if imeWindow with {@code windowTitle} is visible.
*/
- Result isImeWindowVisible(String windowTitle) {
+ public Result isImeWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].imeWindows;
@@ -197,7 +197,7 @@ public class WindowManagerTrace {
/**
* Checks if app window with {@code windowTitle} is on top.
*/
- Result isVisibleAppWindowOnTop(String windowTitle) {
+ public Result isVisibleAppWindowOnTop(String windowTitle) {
String topAppWindow = getTopVisibleAppWindow();
boolean success = topAppWindow.contains(windowTitle);
String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
@@ -207,7 +207,7 @@ public class WindowManagerTrace {
/**
* Checks if app window with {@code windowTitle} is visible.
*/
- Result isAppWindowVisible(String windowTitle) {
+ public Result isAppWindowVisible(String windowTitle) {
final String assertionName = "isAppWindowVisible";
boolean titleFound = false;
StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
index c54396f895e4..3d25fbed5135 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
@@ -28,9 +28,9 @@ import androidx.test.InstrumentationRegistry;
/**
* Helper functions to retrieve system window sizes and positions.
*/
-class WindowUtils {
+public class WindowUtils {
- static Rect getDisplayBounds() {
+ public static Rect getDisplayBounds() {
Point display = new Point();
WindowManager wm =
(WindowManager) InstrumentationRegistry.getContext().getSystemService(
@@ -46,7 +46,7 @@ class WindowUtils {
return wm.getDefaultDisplay().getRotation();
}
- static Rect getDisplayBounds(int requestedRotation) {
+ public static Rect getDisplayBounds(int requestedRotation) {
Rect displayBounds = getDisplayBounds();
int currentDisplayRotation = getCurrentRotation();
@@ -66,7 +66,7 @@ class WindowUtils {
}
- static Rect getAppPosition(int requestedRotation) {
+ public static Rect getAppPosition(int requestedRotation) {
Rect displayBounds = getDisplayBounds();
int currentDisplayRotation = getCurrentRotation();
@@ -85,7 +85,7 @@ class WindowUtils {
return new Rect(0, 0, displayBounds.width(), displayBounds.height());
}
- static Rect getStatusBarPosition(int requestedRotation) {
+ public static Rect getStatusBarPosition(int requestedRotation) {
Resources resources = InstrumentationRegistry.getContext().getResources();
String resourceName;
Rect displayBounds = getDisplayBounds();
@@ -104,7 +104,7 @@ class WindowUtils {
return new Rect(0, 0, width, height);
}
- static Rect getNavigationBarPosition(int requestedRotation) {
+ public static Rect getNavigationBarPosition(int requestedRotation) {
Resources resources = InstrumentationRegistry.getContext().getResources();
Rect displayBounds = getDisplayBounds();
int displayWidth = Math.min(displayBounds.width(), displayBounds.height());
@@ -129,13 +129,13 @@ class WindowUtils {
}
}
- static int getNavigationBarHeight() {
+ public static int getNavigationBarHeight() {
Resources resources = InstrumentationRegistry.getContext().getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId);
}
- static int getDockedStackDividerInset() {
+ public static int getDockedStackDividerInset() {
Resources resources = InstrumentationRegistry.getContext().getResources();
int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
"android");
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
index e76da6e90834..064cc2702f39 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java
index e00a2474556c..6821ff02e371 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker;
+package com.android.server.wm.flicker.helpers;
import static android.os.SystemClock.sleep;
import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
@@ -44,6 +44,8 @@ import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
+import com.android.server.wm.flicker.WindowUtils;
+
/**
* Collection of UI Automation helper functions.
*/
@@ -70,14 +72,14 @@ public class AutomationUtils {
* This removes some delays when using the UIAutomator library required to create fast UI
* transitions.
*/
- static void setFastWait() {
+ public static void setFastWait() {
Configurator.getInstance().setWaitForIdleTimeout(0);
}
/**
* Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
*/
- static void setDefaultWait() {
+ public static void setDefaultWait() {
Configurator.getInstance().setWaitForIdleTimeout(10000);
}
@@ -124,7 +126,7 @@ public class AutomationUtils {
device.waitForIdle();
}
- static void clearRecents(UiDevice device) {
+ public static void clearRecents(UiDevice device) {
if (isQuickstepEnabled(device)) {
openQuickstep(device);
@@ -201,7 +203,7 @@ public class AutomationUtils {
sleep(2000);
}
- static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
+ public static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
assertNotNull("Unable to find Split screen divider", divider);
@@ -218,7 +220,7 @@ public class AutomationUtils {
sleep(2000);
}
- static void closePipWindow(UiDevice device) {
+ public static void closePipWindow(UiDevice device) {
UiObject2 pipWindow = device.findObject(
By.res(SYSTEMUI_PACKAGE, "background"));
pipWindow.click();
@@ -229,7 +231,7 @@ public class AutomationUtils {
sleep(2000);
}
- static void expandPipWindow(UiDevice device) {
+ public static void expandPipWindow(UiDevice device) {
UiObject2 pipWindow = device.findObject(
By.res(SYSTEMUI_PACKAGE, "background"));
pipWindow.click();
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
index c55d068b41b8..da75b3e86d6b 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
@@ -16,21 +16,22 @@
package com.android.server.wm.flicker.monitor;
-import android.os.IBinder;
-import android.os.Parcel;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
/**
* Captures Layers trace from SurfaceFlinger.
*/
public class LayersTraceMonitor extends TraceMonitor {
- private static final String TAG = "LayersTraceMonitor";
- private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+ private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
public LayersTraceMonitor() {
- traceFileName = "layers_trace.pb";
+ this(OUTPUT_DIR.toString());
+ }
+
+ public LayersTraceMonitor(String outputDir) {
+ super(outputDir, "layers_trace.pb");
}
@Override
@@ -45,30 +46,19 @@ public class LayersTraceMonitor extends TraceMonitor {
@Override
public boolean isEnabled() throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026,
- data, reply, 0 /* flags */);
- return reply.readBoolean();
+ try {
+ return mWm.isLayerTracing();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return false;
}
private void setEnabled(boolean isEnabled) {
- Parcel data = null;
try {
- if (mSurfaceFlinger != null) {
- data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(isEnabled ? 1 : 0);
- mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
- data, null, 0 /* flags */);
- }
+ mWm.setLayerTracing(isEnabled);
} catch (RemoteException e) {
- Log.e(TAG, "Could not set layer tracing." + e.toString());
- } finally {
- if (data != null) {
- data.recycle();
- }
+ e.printStackTrace();
}
}
}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
index 4787586777ae..dce1c2739b15 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
@@ -20,25 +20,25 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-import android.support.annotation.VisibleForTesting;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
/**
* Captures screen contents and saves it as a mp4 video file.
*/
public class ScreenRecorder implements ITransitionMonitor {
@VisibleForTesting
- static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
+ public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
private static final String TAG = "FLICKER";
private Thread recorderThread;
@VisibleForTesting
- static Path getPath(String testTag) {
+ public static Path getPath(String testTag) {
return OUTPUT_DIR.resolve(testTag + ".mp4");
}
@@ -69,8 +69,10 @@ public class ScreenRecorder implements ITransitionMonitor {
@Override
public Path save(String testTag) {
try {
- return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
+ Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
REPLACE_EXISTING);
+ Log.i(TAG, "Video saved to " + targetPath.toString());
+ return targetPath;
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
index 0e154ecd5d4d..1ba36bba92ef 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
@@ -20,7 +20,7 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import android.os.RemoteException;
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.VisibleForTesting;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -34,9 +34,15 @@ public abstract class TraceMonitor implements ITransitionMonitor {
public static final String TAG = "FLICKER";
private static final String TRACE_DIR = "/data/misc/wmtrace/";
- String traceFileName;
+ private Path mOutputDir;
+ public String mTraceFileName;
- abstract boolean isEnabled() throws RemoteException;
+ public abstract boolean isEnabled() throws RemoteException;
+
+ public TraceMonitor(String outputDir, String traceFileName) {
+ mOutputDir = Paths.get(outputDir);
+ mTraceFileName = traceFileName;
+ }
/**
* Saves trace file to the external storage directory suffixing the name with the testtag
@@ -53,14 +59,16 @@ public abstract class TraceMonitor implements ITransitionMonitor {
public Path save(String testTag) {
OUTPUT_DIR.toFile().mkdirs();
Path traceFileCopy = getOutputTraceFilePath(testTag);
+
+ // Read the input stream fully.
String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
- traceFileName, traceFileCopy.toString());
+ mTraceFileName, traceFileCopy.toString());
runShellCommand(copyCommand);
return traceFileCopy;
}
@VisibleForTesting
- Path getOutputTraceFilePath(String testTag) {
- return OUTPUT_DIR.resolve(traceFileName + "_" + testTag);
+ public Path getOutputTraceFilePath(String testTag) {
+ return mOutputDir.resolve(mTraceFileName + "_" + testTag);
}
}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
index ae160b68c976..11de4aa86343 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
@@ -24,16 +24,20 @@ import android.view.WindowManagerGlobal;
* Captures WindowManager trace from WindowManager.
*/
public class WindowManagerTraceMonitor extends TraceMonitor {
- private IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
public WindowManagerTraceMonitor() {
- traceFileName = "wm_trace.pb";
+ this(OUTPUT_DIR.toString());
+ }
+
+ public WindowManagerTraceMonitor(String outputDir) {
+ super(outputDir, "wm_trace.pb");
}
@Override
public void start() {
try {
- wm.startWindowTrace();
+ mWm.startWindowTrace();
} catch (RemoteException e) {
throw new RuntimeException("Could not start trace", e);
}
@@ -42,7 +46,7 @@ public class WindowManagerTraceMonitor extends TraceMonitor {
@Override
public void stop() {
try {
- wm.stopWindowTrace();
+ mWm.stopWindowTrace();
} catch (RemoteException e) {
throw new RuntimeException("Could not stop trace", e);
}
@@ -50,6 +54,6 @@ public class WindowManagerTraceMonitor extends TraceMonitor {
@Override
public boolean isEnabled() throws RemoteException{
- return wm.isWindowTraceEnabled();
+ return mWm.isWindowTraceEnabled();
}
}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
index dd6fed04d3e6..f31238477e95 100644
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.monitor;
-import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.wakeUpAndGoToHomeScreen;
import androidx.test.InstrumentationRegistry;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
index 65888acc184b..5cf2c1cd6827 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -19,12 +19,12 @@ package com.android.server.wm.flicker;
import static android.os.SystemClock.sleep;
import static android.view.Surface.rotationToString;
-import static com.android.server.wm.flicker.AutomationUtils.clearRecents;
-import static com.android.server.wm.flicker.AutomationUtils.closePipWindow;
-import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen;
-import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow;
-import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen;
-import static com.android.server.wm.flicker.AutomationUtils.stopPackage;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.stopPackage;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +40,7 @@ import android.view.Surface;
import androidx.test.InstrumentationRegistry;
import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+import com.android.server.wm.flicker.helpers.AutomationUtils;
/**
* Collection of common transitions which can be used to test different apps or scenarios.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
index 00e11c0cef41..8c9d6b4dc7a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker;
-import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait;
+import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait;
import static com.google.common.truth.Truth.assertWithMessage;
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 88d92c4d94fe..0b75039cf69f 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -23,6 +23,7 @@ android_test {
"androidx.test.rules",
"services.core",
"services.net",
+ "truth-prebuilt",
],
libs: ["android.test.runner"],
jni_libs: [
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 6c05bb8e8690..79f5095010e8 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -18,9 +18,8 @@ package com.android.server;
import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -65,7 +64,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-// TODO: Use Truth in tests.
/**
* Test PackageWatchdog.
*/
@@ -119,13 +117,12 @@ public class PackageWatchdogTest {
TestObserver observer = new TestObserver(OBSERVER_NAME_1);
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// The failed packages should be the same as the registered ones to ensure registration is
// done successfully
- assertEquals(1, observer.mHealthCheckFailedPackages.size());
- assertTrue(observer.mHealthCheckFailedPackages.contains(APP_A));
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
}
@Test
@@ -136,17 +133,14 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
- new VersionedPackage(APP_B, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+ new VersionedPackage(APP_B, VERSION_CODE)));
// The failed packages should be the same as the registered ones to ensure registration is
// done successfully
- assertEquals(1, observer1.mHealthCheckFailedPackages.size());
- assertEquals(2, observer2.mHealthCheckFailedPackages.size());
- assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
- assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_A));
- assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_B));
+ assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
+ assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
}
@Test
@@ -156,11 +150,11 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// We should have no failed packages to ensure unregistration is done successfully
- assertEquals(0, observer.mHealthCheckFailedPackages.size());
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
}
@Test
@@ -172,13 +166,13 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.unregisterHealthObserver(observer2);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// observer1 should receive failed packages as intended.
- assertEquals(1, observer1.mHealthCheckFailedPackages.size());
+ assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
// observer2 should have no failed packages to ensure unregistration is done successfully
- assertEquals(0, observer2.mHealthCheckFailedPackages.size());
+ assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
}
@Test
@@ -188,11 +182,11 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// We should have no failed packages for the fatal failure is raised after expiration
- assertEquals(0, observer.mHealthCheckFailedPackages.size());
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
}
@Test
@@ -204,13 +198,13 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION);
moveTimeForwardAndDispatch(SHORT_DURATION);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// We should have no failed packages for the fatal failure is raised after expiration
- assertEquals(0, observer1.mHealthCheckFailedPackages.size());
+ assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
// We should have failed packages since observer2 hasn't expired
- assertEquals(1, observer2.mHealthCheckFailedPackages.size());
+ assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A);
}
/** Observing already observed package extends the observation time. */
@@ -231,12 +225,11 @@ public class PackageWatchdogTest {
// Then advance time such that it should have expired were it not for the second observation
moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify that we receive failed packages as expected for APP_A not expired
- assertEquals(1, observer.mHealthCheckFailedPackages.size());
- assertTrue(observer.mHealthCheckFailedPackages.contains(APP_A));
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
}
/**
@@ -257,17 +250,14 @@ public class PackageWatchdogTest {
// Then resume observer1 and observer2
watchdog2.registerHealthObserver(observer1);
watchdog2.registerHealthObserver(observer2);
- raiseFatalFailure(watchdog2, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
- new VersionedPackage(APP_B, VERSION_CODE)));
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog2,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+ new VersionedPackage(APP_B, VERSION_CODE)));
// We should receive failed packages as expected to ensure observers are persisted and
// resumed correctly
- assertEquals(1, observer1.mHealthCheckFailedPackages.size());
- assertEquals(2, observer2.mHealthCheckFailedPackages.size());
- assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
- assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
- assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_B));
+ assertThat(observer1.mHealthCheckFailedPackages).containsExactly(APP_A);
+ assertThat(observer2.mHealthCheckFailedPackages).containsExactly(APP_A, APP_B);
}
/**
@@ -291,8 +281,8 @@ public class PackageWatchdogTest {
mTestLooper.dispatchAll();
// Verify that observers are not notified
- assertEquals(0, observer1.mMitigatedPackages.size());
- assertEquals(0, observer2.mMitigatedPackages.size());
+ assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
+ assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
}
/**
@@ -310,14 +300,12 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
// Then fail APP_C (not observed) above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)));
-
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)));
// Verify that observers are not notified
- assertEquals(0, observer1.mMitigatedPackages.size());
- assertEquals(0, observer2.mMitigatedPackages.size());
+ assertThat(observer1.mHealthCheckFailedPackages).isEmpty();
+ assertThat(observer2.mHealthCheckFailedPackages).isEmpty();
}
/**
@@ -342,14 +330,11 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A (different version) above the threshold
- raiseFatalFailure(watchdog,
+ raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(new VersionedPackage(APP_A, differentVersionCode)));
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
-
// Verify that observers are not notified
- assertEquals(0, observer.mMitigatedPackages.size());
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
}
@@ -379,13 +364,11 @@ public class PackageWatchdogTest {
SHORT_DURATION);
// Then fail all apps above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
- new VersionedPackage(APP_B, VERSION_CODE),
- new VersionedPackage(APP_C, VERSION_CODE),
- new VersionedPackage(APP_D, VERSION_CODE)));
-
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+ new VersionedPackage(APP_B, VERSION_CODE),
+ new VersionedPackage(APP_C, VERSION_CODE),
+ new VersionedPackage(APP_D, VERSION_CODE)));
// Verify least impact observers are notifed of package failures
List<String> observerNonePackages = observerNone.mMitigatedPackages;
@@ -394,16 +377,13 @@ public class PackageWatchdogTest {
List<String> observerLowPackages = observerLow.mMitigatedPackages;
// APP_D failure observed by only observerNone is not caught cos its impact is none
- assertEquals(0, observerNonePackages.size());
+ assertThat(observerNonePackages).isEmpty();
// APP_C failure is caught by observerHigh cos it's the lowest impact observer
- assertEquals(1, observerHighPackages.size());
- assertEquals(APP_C, observerHighPackages.get(0));
+ assertThat(observerHighPackages).containsExactly(APP_C);
// APP_B failure is caught by observerMid cos it's the lowest impact observer
- assertEquals(1, observerMidPackages.size());
- assertEquals(APP_B, observerMidPackages.get(0));
+ assertThat(observerMidPackages).containsExactly(APP_B);
// APP_A failure is caught by observerLow cos it's the lowest impact observer
- assertEquals(1, observerLowPackages.size());
- assertEquals(APP_A, observerLowPackages.get(0));
+ assertThat(observerLowPackages).containsExactly(APP_A);
}
/**
@@ -430,14 +410,12 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify only observerFirst is notifed
- assertEquals(1, observerFirst.mMitigatedPackages.size());
- assertEquals(APP_A, observerFirst.mMitigatedPackages.get(0));
- assertEquals(0, observerSecond.mMitigatedPackages.size());
+ assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
+ assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, next action it has is high impact
observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH;
@@ -445,14 +423,12 @@ public class PackageWatchdogTest {
observerSecond.mMitigatedPackages.clear();
// Then fail APP_A again above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify only observerSecond is notifed cos it has least impact
- assertEquals(1, observerSecond.mMitigatedPackages.size());
- assertEquals(APP_A, observerSecond.mMitigatedPackages.get(0));
- assertEquals(0, observerFirst.mMitigatedPackages.size());
+ assertThat(observerSecond.mMitigatedPackages).containsExactly(APP_A);
+ assertThat(observerFirst.mMitigatedPackages).isEmpty();
// After observerSecond handles failure, it has no further actions
observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
@@ -460,14 +436,12 @@ public class PackageWatchdogTest {
observerSecond.mMitigatedPackages.clear();
// Then fail APP_A again above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify only observerFirst is notifed cos it has the only action
- assertEquals(1, observerFirst.mMitigatedPackages.size());
- assertEquals(APP_A, observerFirst.mMitigatedPackages.get(0));
- assertEquals(0, observerSecond.mMitigatedPackages.size());
+ assertThat(observerFirst.mMitigatedPackages).containsExactly(APP_A);
+ assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, it too has no further actions
observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
@@ -475,13 +449,12 @@ public class PackageWatchdogTest {
observerSecond.mMitigatedPackages.clear();
// Then fail APP_A again above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify no observer is notified cos no actions left
- assertEquals(0, observerFirst.mMitigatedPackages.size());
- assertEquals(0, observerSecond.mMitigatedPackages.size());
+ assertThat(observerFirst.mMitigatedPackages).isEmpty();
+ assertThat(observerSecond.mMitigatedPackages).isEmpty();
}
/**
@@ -500,15 +473,12 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
- raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-
- // Run handler so package failures are dispatched to observers
- mTestLooper.dispatchAll();
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
// Verify only one observer is notifed
- assertEquals(1, observer1.mMitigatedPackages.size());
- assertEquals(APP_A, observer1.mMitigatedPackages.get(0));
- assertEquals(0, observer2.mMitigatedPackages.size());
+ assertThat(observer1.mMitigatedPackages).containsExactly(APP_A);
+ assertThat(observer2.mMitigatedPackages).isEmpty();
}
/**
@@ -537,9 +507,7 @@ public class PackageWatchdogTest {
// Verify we requested health checks for APP_A and APP_B
List<String> requestedPackages = controller.getRequestedPackages();
- assertEquals(2, requestedPackages.size());
- assertEquals(APP_A, requestedPackages.get(0));
- assertEquals(APP_B, requestedPackages.get(1));
+ assertThat(requestedPackages).containsExactly(APP_A, APP_B);
// Then health check passed for APP_A (observer1 is aware)
controller.setPackagePassed(APP_A);
@@ -554,18 +522,16 @@ public class PackageWatchdogTest {
moveTimeForwardAndDispatch(SHORT_DURATION);
// Verify we cancelled all requests on expiry
- assertEquals(0, controller.getRequestedPackages().size());
+ assertThat(controller.getRequestedPackages()).isEmpty();
// Verify observer1 is not notified
- assertEquals(0, observer1.mMitigatedPackages.size());
+ assertThat(observer1.mMitigatedPackages).isEmpty();
// Verify observer2 is notifed because health checks for APP_B never passed
- assertEquals(1, observer2.mMitigatedPackages.size());
- assertEquals(APP_B, observer2.mMitigatedPackages.get(0));
+ assertThat(observer2.mMitigatedPackages).containsExactly(APP_B);
// Verify observer3 is notifed because health checks for APP_A did not pass before expiry
- assertEquals(1, observer3.mMitigatedPackages.size());
- assertEquals(APP_A, observer3.mMitigatedPackages.get(0));
+ assertThat(observer3.mMitigatedPackages).containsExactly(APP_A);
}
/**
@@ -592,9 +558,7 @@ public class PackageWatchdogTest {
// Verify we requested health checks for APP_A and APP_B
List<String> requestedPackages = controller.getRequestedPackages();
- assertEquals(2, requestedPackages.size());
- assertEquals(APP_A, requestedPackages.get(0));
- assertEquals(APP_B, requestedPackages.get(1));
+ assertThat(requestedPackages).containsExactly(APP_A, APP_B);
// Disable explicit health checks (marks APP_A and APP_B as passed)
setExplicitHealthCheckEnabled(false);
@@ -603,13 +567,13 @@ public class PackageWatchdogTest {
mTestLooper.dispatchAll();
// Verify all checks are cancelled
- assertEquals(0, controller.getRequestedPackages().size());
+ assertThat(controller.getRequestedPackages()).isEmpty();
// Then expire APP_A
moveTimeForwardAndDispatch(SHORT_DURATION);
// Verify APP_A is not failed (APP_B) is not expired yet
- assertEquals(0, observer.mMitigatedPackages.size());
+ assertThat(observer.mMitigatedPackages).isEmpty();
// Re-enable explicit health checks
setExplicitHealthCheckEnabled(true);
@@ -618,7 +582,7 @@ public class PackageWatchdogTest {
mTestLooper.dispatchAll();
// Verify no requests are made cos APP_A is expired and APP_B was marked as passed
- assertEquals(0, controller.getRequestedPackages().size());
+ assertThat(controller.getRequestedPackages()).isEmpty();
// Then set new supported packages
controller.setSupportedPackages(Arrays.asList(APP_C));
@@ -630,15 +594,13 @@ public class PackageWatchdogTest {
// Verify requests are only made for APP_C
requestedPackages = controller.getRequestedPackages();
- assertEquals(1, requestedPackages.size());
- assertEquals(APP_C, requestedPackages.get(0));
+ assertThat(requestedPackages).containsExactly(APP_C);
// Then expire APP_A and APP_C
moveTimeForwardAndDispatch(SHORT_DURATION);
// Verify only APP_C is failed because explicit health checks was not supported for APP_A
- assertEquals(1, observer.mMitigatedPackages.size());
- assertEquals(APP_C, observer.mMitigatedPackages.get(0));
+ assertThat(observer.mMitigatedPackages).containsExactly(APP_C);
}
/**
@@ -662,8 +624,7 @@ public class PackageWatchdogTest {
moveTimeForwardAndDispatch(SHORT_DURATION);
// Verify that health check is failed
- assertEquals(1, observer.mMitigatedPackages.size());
- assertEquals(APP_A, observer.mMitigatedPackages.get(0));
+ assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
// Then clear failed packages and start observing a random package so requests are synced
// and PackageWatchdog#onSupportedPackages is called and APP_A has a chance to fail again
@@ -672,7 +633,7 @@ public class PackageWatchdogTest {
watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
// Verify that health check failure is not notified again
- assertTrue(observer.mMitigatedPackages.isEmpty());
+ assertThat(observer.mMitigatedPackages).isEmpty();
}
/** Tests {@link MonitoredPackage} health check state transitions. */
@@ -688,36 +649,38 @@ public class PackageWatchdogTest {
// Verify transition: inactive -> active -> passed
// Verify initially inactive
- assertEquals(HealthCheckState.INACTIVE, m1.getHealthCheckStateLocked());
+ assertThat(m1.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
// Verify still inactive, until we #setHealthCheckActiveLocked
- assertEquals(HealthCheckState.INACTIVE, m1.handleElapsedTimeLocked(SHORT_DURATION));
+ assertThat(m1.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.INACTIVE);
// Verify now active
- assertEquals(HealthCheckState.ACTIVE, m1.setHealthCheckActiveLocked(SHORT_DURATION));
+ assertThat(m1.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo(
+ HealthCheckState.ACTIVE);
// Verify now passed
- assertEquals(HealthCheckState.PASSED, m1.tryPassHealthCheckLocked());
+ assertThat(m1.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.PASSED);
// Verify transition: inactive -> active -> failed
// Verify initially inactive
- assertEquals(HealthCheckState.INACTIVE, m2.getHealthCheckStateLocked());
+ assertThat(m2.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
// Verify now active
- assertEquals(HealthCheckState.ACTIVE, m2.setHealthCheckActiveLocked(SHORT_DURATION));
+ assertThat(m2.setHealthCheckActiveLocked(SHORT_DURATION)).isEqualTo(
+ HealthCheckState.ACTIVE);
// Verify now failed
- assertEquals(HealthCheckState.FAILED, m2.handleElapsedTimeLocked(SHORT_DURATION));
+ assertThat(m2.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.FAILED);
// Verify transition: inactive -> failed
// Verify initially inactive
- assertEquals(HealthCheckState.INACTIVE, m3.getHealthCheckStateLocked());
+ assertThat(m3.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.INACTIVE);
// Verify now failed because package expired
- assertEquals(HealthCheckState.FAILED, m3.handleElapsedTimeLocked(LONG_DURATION));
+ assertThat(m3.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.FAILED);
// Verify remains failed even when asked to pass
- assertEquals(HealthCheckState.FAILED, m3.tryPassHealthCheckLocked());
+ assertThat(m3.tryPassHealthCheckLocked()).isEqualTo(HealthCheckState.FAILED);
// Verify transition: passed
- assertEquals(HealthCheckState.PASSED, m4.getHealthCheckStateLocked());
+ assertThat(m4.getHealthCheckStateLocked()).isEqualTo(HealthCheckState.PASSED);
// Verify remains passed even if health check fails
- assertEquals(HealthCheckState.PASSED, m4.handleElapsedTimeLocked(SHORT_DURATION));
+ assertThat(m4.handleElapsedTimeLocked(SHORT_DURATION)).isEqualTo(HealthCheckState.PASSED);
// Verify remains passed even if package expires
- assertEquals(HealthCheckState.PASSED, m4.handleElapsedTimeLocked(LONG_DURATION));
+ assertThat(m4.handleElapsedTimeLocked(LONG_DURATION)).isEqualTo(HealthCheckState.PASSED);
}
@Test
@@ -736,8 +699,142 @@ public class PackageWatchdogTest {
mTestLooper.dispatchAll();
// Verify the NetworkStack observer is notified
- assertEquals(1, observer.mMitigatedPackages.size());
- assertEquals(APP_A, observer.mMitigatedPackages.get(0));
+ assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
+ }
+
+ /** Test default values are used when device property is invalid. */
+ @Test
+ public void testInvalidConfig_watchdogTriggerFailureCount() {
+ adoptShellPermissions(
+ Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(-1), /*makeDefault*/false);
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ // Fail APP_A below the threshold which should not trigger package failures
+ for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ }
+ mTestLooper.dispatchAll();
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
+
+ // One more to trigger the package failure
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
+ }
+
+ /** Test default values are used when device property is invalid. */
+ @Test
+ public void testInvalidConfig_watchdogTriggerDurationMillis() {
+ adoptShellPermissions(
+ Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(2), /*makeDefault*/false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
+ Integer.toString(-1), /*makeDefault*/false);
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // We shouldn't receive APP_A since the interval of 2 failures is greater than
+ // DEFAULT_TRIGGER_FAILURE_DURATION_MS.
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
+
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // We should receive APP_B since the interval of 2 failures is less than
+ // DEFAULT_TRIGGER_FAILURE_DURATION_MS.
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_B);
+ }
+
+ /**
+ * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
+ * an invalid durationMs.
+ */
+ @Test
+ public void testInvalidMonitoringDuration_beforeExpiry() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ // Note: Don't move too close to the expiration time otherwise the handler will be thrashed
+ // by PackageWatchdog#scheduleNextSyncStateLocked which keeps posting runnables with very
+ // small timeouts.
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS - 100);
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+
+ // We should receive APP_A since the observer hasn't expired
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
+ }
+
+ /**
+ * Test default monitoring duration is used when PackageWatchdog#startObservingHealth is offered
+ * an invalid durationMs.
+ */
+ @Test
+ public void testInvalidMonitoringDuration_afterExpiry() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), -1);
+ moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_OBSERVING_DURATION_MS + 1);
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+
+ // We should receive nothing since the observer has expired
+ assertThat(observer.mHealthCheckFailedPackages).isEmpty();
+ }
+
+ /** Test we are notified when enough failures are triggered within any window. */
+ @Test
+ public void testFailureTriggerWindow() {
+ adoptShellPermissions(
+ Manifest.permission.WRITE_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(3), /*makeDefault*/false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PackageWatchdog.PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
+ Integer.toString(1000), /*makeDefault*/false);
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE);
+ // Raise 2 failures at t=0 and t=900 respectively
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+ moveTimeForwardAndDispatch(900);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // Raise 2 failures at t=1100
+ moveTimeForwardAndDispatch(200);
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+ mTestLooper.dispatchAll();
+
+ // We should receive APP_A since there are 3 failures within 1000ms window
+ assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
}
private void adoptShellPermissions(String... permissions) {
@@ -773,10 +870,12 @@ public class PackageWatchdogTest {
}
/** Trigger package failures above the threshold. */
- private void raiseFatalFailure(PackageWatchdog watchdog, List<VersionedPackage> packages) {
+ private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
+ List<VersionedPackage> packages) {
for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
watchdog.onPackageFailure(packages);
}
+ mTestLooper.dispatchAll();
}
private PackageWatchdog createWatchdog() {
@@ -791,13 +890,13 @@ public class PackageWatchdogTest {
new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
mConnectivityModuleConnector, mTestClock);
// Verify controller is not automatically started
- assertFalse(controller.mIsEnabled);
+ assertThat(controller.mIsEnabled).isFalse();
if (withPackagesReady) {
// Only capture the NetworkStack callback for the latest registered watchdog
reset(mConnectivityModuleConnector);
watchdog.onPackagesReady();
// Verify controller by default is started when packages are ready
- assertTrue(controller.mIsEnabled);
+ assertThat(controller.mIsEnabled).isTrue();
verify(mConnectivityModuleConnector).registerHealthListener(
mConnectivityModuleCallbackCaptor.capture());
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 502aa97bfc68..e91abb6c4a44 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -20,8 +20,6 @@ java_defaults {
"libdl_android",
"libhidl-gen-utils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"libjsoncpp",
"liblog",
"liblzma",
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 5e06818d7a13..e36668e5a043 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -53,9 +53,9 @@ using ::android::ConfigDescription;
using ::android::ResTable_config;
using ::android::StringPiece;
using ::android::base::ReadFileToString;
-using ::android::base::WriteStringToFile;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
+using ::android::base::WriteStringToFile;
namespace aapt {
@@ -300,29 +300,7 @@ class Optimizer {
OptimizeContext* context_;
};
-bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string contents;
- if (!ReadFileToString(path, &contents, true)) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "failed to parse whitelist from config file: " << path);
- return false;
- }
- for (StringPiece resource_name : util::Tokenize(contents, ',')) {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.to_string());
- }
- return true;
-}
-
-bool ExtractConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string content;
- if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
- context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
- return false;
- }
-
+bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) {
size_t line_no = 0;
for (StringPiece line : util::Tokenize(content, '\n')) {
line_no++;
@@ -351,15 +329,24 @@ bool ExtractConfig(const std::string& path, OptimizeContext* context,
for (StringPiece directive : util::Tokenize(directives, ',')) {
if (directive == "remove") {
options->resources_blacklist.insert(resource_name.ToResourceName());
- } else if (directive == "no_obfuscate") {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.entry.to_string());
+ } else if (directive == "no_collapse" || directive == "no_obfuscate") {
+ options->table_flattener_options.name_collapse_exemptions.insert(
+ resource_name.ToResourceName());
}
}
}
return true;
}
+bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading config file");
+ return false;
+ }
+ return ParseConfig(content, context, options);
+}
+
bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
OptimizeOptions* out_options) {
const xml::XmlResource* manifest = apk->GetManifest();
@@ -467,15 +454,6 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
}
}
- if (options_.table_flattener_options.collapse_key_stringpool) {
- if (whitelist_path_) {
- std::string& path = whitelist_path_.value();
- if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
- return 1;
- }
- }
- }
-
if (resources_config_path_) {
std::string& path = resources_config_path_.value();
if (!ExtractConfig(path, &context, &options_)) {
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 0be7dad18380..5070ccc8afbf 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -78,10 +78,6 @@ class OptimizeCommand : public Command {
"All the resources that would be unused on devices of the given densities will be \n"
"removed from the APK.",
&target_densities_);
- AddOptionalFlag("--whitelist-path",
- "Path to the whitelist.cfg file containing whitelisted resources \n"
- "whose names should not be altered in final resource tables.",
- &whitelist_path_);
AddOptionalFlag("--resources-config-path",
"Path to the resources.cfg file containing the list of resources and \n"
"directives to each resource. \n"
@@ -104,11 +100,13 @@ class OptimizeCommand : public Command {
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.",
&options_.table_flattener_options.use_sparse_entries);
- AddOptionalSwitch("--enable-resource-obfuscation",
- "Enables obfuscation of key string pool to single value",
+ AddOptionalSwitch("--collapse-resource-names",
+ "Collapses resource names to a single value in the key string pool. Resources can \n"
+ "be exempted using the \"no_collapse\" directive in a file specified by "
+ "--resources-config-path.",
&options_.table_flattener_options.collapse_key_stringpool);
- AddOptionalSwitch("--enable-resource-path-shortening",
- "Enables shortening of the path of the resources inside the APK.",
+ AddOptionalSwitch("--shorten-resource-paths",
+ "Shortens the paths of resources inside the APK.",
&options_.shorten_resource_paths);
AddOptionalFlag("--resource-path-shortening-map",
"Path to output the map of old resource paths to shortened paths.",
@@ -125,7 +123,6 @@ class OptimizeCommand : public Command {
const std::string &file_path);
Maybe<std::string> config_path_;
- Maybe<std::string> whitelist_path_;
Maybe<std::string> resources_config_path_;
Maybe<std::string> target_densities_;
std::vector<std::string> configs_;
diff --git a/tools/aapt2/cmd/Optimize_test.cpp b/tools/aapt2/cmd/Optimize_test.cpp
new file mode 100644
index 000000000000..ac681e85b3d6
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include "Optimize.h"
+
+#include "AppInfo.h"
+#include "Diagnostics.h"
+#include "LoadedApk.h"
+#include "Resource.h"
+#include "test/Test.h"
+
+using testing::Contains;
+using testing::Eq;
+
+namespace aapt {
+
+bool ParseConfig(const std::string&, IAaptContext*, OptimizeOptions*);
+
+using OptimizeTest = CommandTestFixture;
+
+TEST_F(OptimizeTest, ParseConfigWithNoCollapseExemptions) {
+ const std::string& content = R"(
+string/foo#no_collapse
+dimen/bar#no_collapse
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+TEST_F(OptimizeTest, ParseConfigWithNoObfuscateExemptions) {
+ const std::string& content = R"(
+string/foo#no_obfuscate
+dimen/bar#no_obfuscate
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index b9321174100b..58e232c33985 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -228,14 +228,15 @@ class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
- bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources)
+ bool collapse_key_stringpool,
+ const std::set<ResourceName>& name_collapse_exemptions)
: context_(context),
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
use_sparse_entries_(use_sparse_entries),
collapse_key_stringpool_(collapse_key_stringpool),
- whitelisted_resources_(whitelisted_resources) {
+ name_collapse_exemptions_(name_collapse_exemptions) {
}
bool FlattenPackage(BigBuffer* buffer) {
@@ -652,11 +653,12 @@ class PackageFlattener {
for (ResourceEntry* entry : sorted_entries) {
uint32_t local_key_index;
+ ResourceName resource_name({}, type->type, entry->name);
if (!collapse_key_stringpool_ ||
- whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) {
+ name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
} else {
- // resource isn't whitelisted, add it as obfuscated value
+ // resource isn't exempt from collapse, add it as obfuscated value
local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
}
// Group values by configuration.
@@ -712,7 +714,7 @@ class PackageFlattener {
StringPool type_pool_;
StringPool key_pool_;
bool collapse_key_stringpool_;
- const std::set<std::string>& whitelisted_resources_;
+ const std::set<ResourceName>& name_collapse_exemptions_;
};
} // namespace
@@ -760,7 +762,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
PackageFlattener flattener(context, package.get(), &table->included_packages_,
options_.use_sparse_entries, options_.collapse_key_stringpool,
- options_.whitelisted_resources);
+ options_.name_collapse_exemptions);
if (!flattener.FlattenPackage(&package_buffer)) {
return false;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 73c17295556b..4360db190146 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -19,6 +19,7 @@
#include "android-base/macros.h"
+#include "Resource.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
@@ -41,8 +42,8 @@ struct TableFlattenerOptions {
// have name indices that point to this single value
bool collapse_key_stringpool = false;
- // Set of whitelisted resource names to avoid altering in key stringpool
- std::set<std::string> whitelisted_resources;
+ // Set of resources to avoid collapsing to a single entry in key stringpool.
+ std::set<ResourceName> name_collapse_exemptions;
// Map from original resource paths to shortened resource paths.
std::map<std::string, std::string> shortened_path_map;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index a9409235e07a..8fbdd7f27041 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -518,7 +518,7 @@ TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) {
ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -572,7 +572,7 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -591,21 +591,22 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
TableFlattenerOptions options;
options.collapse_key_stringpool = true;
- options.whitelisted_resources.insert("test");
- options.whitelisted_resources.insert("three");
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one",
ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
- Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ // Note that this resource is also named "one", but it's a different type, so gets obfuscated.
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
ResTable_config::CONFIG_VERSION));
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index 0f932f3c34e1..fa2b41adcacb 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -107,7 +107,7 @@ fun main(args: Array<String>) {
println(CODEGEN_VERSION)
System.exit(0)
}
- val file = File(args.last())
+ val file = File(args.last()).absoluteFile
val sourceLinesNoClosingBrace = file.readLines().dropLastWhile {
it.startsWith("}") || it.all(Char::isWhitespace)
}
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 7d50ad10de00..b2cc81391510 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.0"
+const val CODEGEN_VERSION = "1.0.1"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
index 485a0479cbd9..618e4b14e4c5 100644
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
@@ -42,8 +42,9 @@ object ProtoLogTool {
command.javaSourceArgs.forEach { path ->
val file = File(path)
- val code = StaticJavaParser.parse(file)
- val outSrc = transformer.processClass(code)
+ val text = file.readText()
+ val code = StaticJavaParser.parse(text)
+ val outSrc = transformer.processClass(text, code)
val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
.get().nameAsString else ""
val newPath = pack.replace('.', '/') + '/' + file.name
diff --git a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt b/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
index 319a8170dca8..f915ea6eb186 100644
--- a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
@@ -130,6 +130,8 @@ class SourceTransformer(
// Append blank lines to preserve line numbering in file (to allow debugging)
val newLines = LexicalPreservingPrinter.print(parentStmt).count { c -> c == '\n' }
val newStmt = printedIfStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}'
+ // pre-workaround code, see explanation below
+ /*
val inlinedIfStmt = StaticJavaParser.parseStatement(newStmt)
LexicalPreservingPrinter.setup(inlinedIfStmt)
// Replace the original call.
@@ -138,6 +140,27 @@ class SourceTransformer(
throw RuntimeException("Unable to process log call $call " +
"- unable to replace the call.")
}
+ */
+ /** Workaround for a bug in JavaParser (AST tree invalid after replacing a node when using
+ * LexicalPreservingPrinter (https://github.com/javaparser/javaparser/issues/2290).
+ * Replace the code below with the one commended-out above one the issue is resolved. */
+ if (!parentStmt.range.isPresent) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- unable to replace the call.")
+ }
+ val range = parentStmt.range.get()
+ val begin = range.begin.line - 1
+ val oldLines = processedCode.subList(begin, range.end.line)
+ val oldCode = oldLines.joinToString("\n")
+ val newCode = oldCode.replaceRange(
+ offsets[begin] + range.begin.column - 1,
+ oldCode.length - oldLines.lastOrNull()!!.length +
+ range.end.column + offsets[range.end.line - 1], newStmt)
+ newCode.split("\n").forEachIndexed { idx, line ->
+ offsets[begin + idx] += line.length - processedCode[begin + idx].length
+ processedCode[begin + idx] = line
+ }
}
private val inlinePrinter: PrettyPrinter
@@ -153,10 +176,19 @@ class SourceTransformer(
private val protoLogImplClassNode =
StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
+ private var processedCode: MutableList<String> = mutableListOf()
+ private var offsets: IntArray = IntArray(0)
- fun processClass(compilationUnit: CompilationUnit): String {
+ fun processClass(
+ code: String,
+ compilationUnit: CompilationUnit =
+ StaticJavaParser.parse(code)
+ ): String {
+ processedCode = code.split('\n').toMutableList()
+ offsets = IntArray(processedCode.size)
LexicalPreservingPrinter.setup(compilationUnit)
protoLogCallProcessor.process(compilationUnit, this)
- return LexicalPreservingPrinter.print(compilationUnit)
+ // return LexicalPreservingPrinter.print(compilationUnit)
+ return processedCode.joinToString("\n")
}
}
diff --git a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
index 7b8dd9a73fa9..2cd85627b94b 100644
--- a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
@@ -28,6 +28,8 @@ import org.mockito.Mockito
class SourceTransformerTest {
companion object {
private const val PROTO_LOG_IMPL_PATH = "org.example.ProtoLogImpl"
+
+ /* ktlint-disable max-line-length */
private val TEST_CODE = """
package org.example;
@@ -50,6 +52,17 @@ class SourceTransformerTest {
}
""".trimIndent()
+ private val TEST_CODE_MULTICALLS = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
+ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
+ }
+ }
+ """.trimIndent()
+
private val TEST_CODE_NO_PARAMS = """
package org.example;
@@ -60,7 +73,6 @@ class SourceTransformerTest {
}
""".trimIndent()
- /* ktlint-disable max-line-length */
private val TRANSFORMED_CODE_TEXT_ENABLED = """
package org.example;
@@ -83,6 +95,17 @@ class SourceTransformerTest {
}
""".trimIndent()
+ private val TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ }
+ }
+ """.trimIndent()
+
private val TRANSFORMED_CODE_NO_PARAMS = """
package org.example;
@@ -146,7 +169,7 @@ class SourceTransformerTest {
@Test
fun processClass_textEnabled() {
- val code = StaticJavaParser.parse(TEST_CODE)
+ var code = StaticJavaParser.parse(TEST_CODE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -158,7 +181,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -181,8 +205,50 @@ class SourceTransformerTest {
}
@Test
+ fun processClass_textEnabledMulticalls() {
+ var code = StaticJavaParser.parse(TEST_CODE_MULTICALLS)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ val calls = code.findAll(MethodCallExpr::class.java)
+ visitor.processCall(calls[0], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ visitor.processCall(calls[1], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ visitor.processCall(calls[2], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(3, ifStmts.size)
+ val ifStmt = ifStmts[1]
+ assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
+ ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(3, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(6, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("835524026", methodCall.arguments[1].toString())
+ assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
+ assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals(TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED, out)
+ }
+
+ @Test
fun processClass_textEnabledMultiline() {
- val code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -195,7 +261,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -219,7 +286,7 @@ class SourceTransformerTest {
@Test
fun processClass_noParams() {
- val code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
+ var code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -231,7 +298,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -252,7 +320,7 @@ class SourceTransformerTest {
@Test
fun processClass_textDisabled() {
- val code = StaticJavaParser.parse(TEST_CODE)
+ var code = StaticJavaParser.parse(TEST_CODE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -264,7 +332,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -288,7 +357,7 @@ class SourceTransformerTest {
@Test
fun processClass_textDisabledMultiline() {
- val code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -301,7 +370,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -326,7 +396,7 @@ class SourceTransformerTest {
@Test
fun processClass_disabled() {
- val code = StaticJavaParser.parse(TEST_CODE)
+ var code = StaticJavaParser.parse(TEST_CODE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -338,7 +408,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
@@ -349,7 +420,7 @@ class SourceTransformerTest {
@Test
fun processClass_disabledMultiline() {
- val code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
Mockito.`when`(processor.process(any(CompilationUnit::class.java),
any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
@@ -362,7 +433,8 @@ class SourceTransformerTest {
invocation.arguments[0] as CompilationUnit
}
- val out = sourceJarWriter.processClass(code)
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
+ code = StaticJavaParser.parse(out)
val ifStmts = code.findAll(IfStmt::class.java)
assertEquals(1, ifStmts.size)
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a37de00ce7de..0fa0ec7afd91 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1732,13 +1732,12 @@ public class WifiManager {
* <li> When an app is uninstalled, all its suggested networks are discarded. If the device is
* currently connected to a suggested network which is being removed then the device will
* disconnect from that network.</li>
- * <li> No in-place modification of existing suggestions are allowed. Apps are expected to
- * remove suggestions using {@link #removeNetworkSuggestions(List)} and then add the modified
- * suggestion back using this API.</li>
+ * <li> In-place modification of existing suggestions are allowed.
+ * If the provided suggestions {@link WifiNetworkSuggestion#equals(Object)} any previously
+ * provided suggestions by the app. Previous suggestions will be updated</li>
*
* @param networkSuggestions List of network suggestions provided by the app.
* @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values.
- * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
* @throws {@link SecurityException} if the caller is missing required permissions.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)